最深层了解虚表 ida

今天面试被人问了:虚表模型。一听见模型,这俩字,我就一下子就蒙了。像我这种不爱看书的人,回答这种问题,真不是那么容易啊。也不知道,面试管要问什么。就拿爱情公寓里定义就业一样:就业,16周岁以上,具有劳动能力的公民,是指具有劳动能力的公民,依法从事某种有报酬或劳动收入的社会活动....。这不就是背课本嘛。
以下是,我在底层对虚表的理解。

class A
{
	int nA;
	virtual void a()
	{
	};
	virtual int bb()
	{
		return 0;
	};
public:
	~A()
	{
	};
	
};
class B :public A
{
public:
	B::B(int)
	{

	}
	virtual void a()
	{
	}
	virtual void aa()
	{
	}
	virtual int b()
	{
		return 0;
	};
	~B()
	{
	};
};

int _tmain(int argc, _TCHAR* argv[])
{
	B b(1);
	A a();
	A *pA = &b;
	
	//B *pB = &a;   //这个无法编译
         B *pB = (B *)&a;  //这个可以编译
         pB->aa();         //这运行就会出错
	return 0;
}

以下是B的构造函数的反汇编

.text:004114A0 ; void __thiscall B__B(B *this, int __formal)
.text:004114A0 ??0B@@QAE@H@Z   proc near               ; CODE XREF: .text:004110FFj
.text:004114A0                                         ; B::B(int)j
.text:004114A0
.text:004114A0 var_CC          = byte ptr -0CCh
.text:004114A0 this            = dword ptr -8
.text:004114A0 __formal        = dword ptr  8
.text:004114A0
.text:004114A0                 push    ebp
.text:004114A1                 mov     ebp, esp
.text:004114A3                 sub     esp, 0CCh
.text:004114A9                 push    ebx
.text:004114AA                 push    esi
.text:004114AB                 push    edi
.text:004114AC                 push    ecx
.text:004114AD                 lea     edi, [ebp+var_CC]
.text:004114B3                 mov     ecx, 33h
.text:004114B8                 mov     eax, 0CCCCCCCCh
.text:004114BD                 rep stosd
.text:004114BF                 pop     ecx
.text:004114C0                 mov     [ebp+this], ecx
.text:004114C3                 mov     ecx, [ebp+this] ; this
.text:004114C6                 call    A::A(void)                                              调用A的构造函数
.text:004114CB                 mov     eax, [ebp+this]
.text:004114CE                 mov     dword ptr [eax], offset ??_7B@@6B@ ; const B::`vftable'  [this] = virtualtable
.text:004114D4                 mov     eax, [ebp+this]
.text:004114D7                 pop     edi
.text:004114D8                 pop     esi
.text:004114D9                 pop     ebx
.text:004114DA                 add     esp, 0CCh
.text:004114E0                 cmp     ebp, esp
.text:004114E2                 call    j___RTC_CheckEsp
.text:004114E7                 mov     esp, ebp
.text:004114E9                 pop     ebp
.text:004114EA                 retn    4
.text:004114EA ??0B@@QAE@H@Z   endp

以上的大体意思如下。这里的代码是编译器自动加上去了。这说明了,构造函数是没有虚函数一说,也没有必要。

B::B()
{
   A::A();
   [this] = B.VirtualTbale;
}


 

首先,A::A() 里也会初始化一个虚表,如果,构造函数会有虚函数一说的话,那基本就乱了。这也就是先有蛋还是先有鸡一说了。虚表要,在构造函数里初始化,而构造函数会调用到函数里的函数。

但是虑析构函数就是存在的。以下会说明。还是先看虚表是如何结构。

虚表在什么地方呢。[this] = B.VirtualTable 可以看的出。它是在类的前几个字节存放的指针。(32位系统,4字节,64就是8字节)

A 的结构就是

struct
{
  PVOID pVairtualTable;         //是一个指针。这就是虚表
   int        nA;
};

 

首先,A::A() 里也会初始化一个虚表,如果,构造函数会有虚函数一说的话,那基本就乱了。这也就是先有蛋还是先有鸡一说了。虚表要,在构造函数里初始化,而构造函数会调用到函数里的函数。但是虑析构函数就是存在的。

.rdata:00415740 ??_7B@@6B@      dd offset j_?a@B@@UAEXXZ ; B::a(void)
.rdata:00415744                 dd offset j_?aa@A@@EAEHXZ ; A::aa(void)
.rdata:00415748                 dd offset j_?bb@B@@UAEXXZ ; B::bb(void)
.rdata:0041574C                 dd offset j_?b@B@@UAEHXZ ; B::b(void)
.rdata:00415750                  
..rdata:00415758 ??_7A@@6B@     dd offset j_?a@A@@EAEXXZ ; A::a(void)
.rdata:0041575C                 dd offset j_?aa@A@@EAEHXZ ; A::aa(void)


 

一眼就可以看出,B虚表里有4个函数,A里有二个。因为,B继承了A,所以,所以,aa() 表会在B里,但是A.a 由于B自己又定义了一个,所以,那个就会是B::a了。

还有其它两个是自己定义的。

现在看这个,B *pB = &a; 这是编译不过去的。为什么,因为,a里的虚表是没有,B里面所有的。如果编译,那么,pB->bb() 这个调用就,这个就会有问题。

下面说明虚析构函数。

格式是和其它的虚函数一样。也会在虚表里。但其是作为特别存在的。不是说它能力,而是说,它的作用

 

例如:A *pA = new B; delete pA;   如果没有虚析函数,那它只能调用 A的析函数。否则,它就会调用 A和B的析函数。

这就很有用。使用过MFC那个分屏的就知道。CSplitterCtrl.CreateView(int row,int col,CRuntimeClass *pViewClass)创建一个分屏。这个是通过内容进行new 一个类的。那自己无需,也无法delete.因为它是内部进行删除了。如果,没有虚析函数,那它就没法调用析构派生类。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值