对象的构造与虚表

假设有这样的类

 1 class Base
 2 {
 3 public:
 4     Base()
 5     {
 6         clear();
 7     }
 8     virtual ~Base()
 9     {
10         int a = 0;
11     }
12     virtual void fun()
13     {
14         cout << "base" << endl;
15     }
16     void clear()
17     {
18         memset(this,0,sizeof Base);
19     }
20     int m_int32;
21 };
22 class child : public Base
23 {
24 public:
25     virtual ~child()
26     {
27         int a = 0;
28     }
29     virtual void fun()
30     {
31         cout << "child" << endl;
32     }
33 };

vs会把父类,子类的虚表地址存在代码段

每次构造函数和析构函数调用之前,会把各自的虚表地址设回去,

父类析构:

1 00411B36  mov         dword ptr [eax],offset Base::`vftable' (417814h) 
2         int a = 0;
3 00411B3C  mov         dword ptr [a],0 

父类构造:

00411976  mov         dword ptr [eax],offset Base::`vftable' (417814h) 
    {
        clear();
0041197C  mov         ecx,dword ptr [this] 
0041197F  call        Base::clear (411127h) 
    }

子类构造:

00411916  call        Base::Base (411145h) 
0041191B  mov         eax,dword ptr [this] 
0041191E  mov         dword ptr [eax],offset child::`vftable' (417808h) 
00411924  mov         eax,dword ptr [this] 

可得出以下结论:

当new 一个子类时,编译器已经把父类及子类的虚表入口地址保存在代码段中,父类及子类各有一份虚表,也已经在构造之前赋值(子类前面有一些值还不清楚)。

this指针保存的是这个新建类的地址,头4个字节存放虚表的入口地址,先构造父类,把父类的虚表入口地址赋值。按照上面程序例子,之后把父类大小的区域置0。在构造子类,把子类的虚表入口地址写入。

析构的时候,自底向上析构,编译器会先把本类的虚表入口地址赋值,这样做是为了正确的析构本类,因为如果不重新赋值,那就可能调用子类的函数,而此时子类已经析构完成了。

在构造和析构函数中,不能使用虚函数,原因就是每次构造和析构,编译器都会在之前把本类的虚函数入口值重新设置,调用虚函数是没有意义的

posted @ 2012-11-13 10:27  可乐爱上了雪碧  阅读(367)  评论(0编辑  收藏  举报