vs2008下C++对象内存布局(4):多重继承

来源:互联网 发布:电脑淘宝卖家中心在哪 编辑:程序博客网 时间:2024/04/29 06:45

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

 

本文适用于

Xp sp3

Vs2008

 

欢迎转载,但请保留作者信息

 

这回我们考虑多重继承的情况:

class CParentA

{

public:

     int parenta_a;

     int parenta_b;

 

public:

     virtual void parenta_f1()

     {

         parenta_a = 0x10;

     }

     virtual void parenta_f2()

     {

         parenta_b = 0x20;

     }

};

 

class CParentB

{

public:

     int parentb_a;

     int parentb_b;

 

public:

     virtual void parentb_f1()

     {

         parentb_a = 0x30;

     }

     virtual void parentb_f2()

     {

         parentb_b = 0x40;

     }

};

 

class CChild : public CParentA, public CParentB

{

public:

     int child_a;

     int child_b;

 

public:

     virtual void parenta_f1()

     {

         child_a = 0x50;

     }

     virtual void parenta_f2()

     {

         child_b = 0x60;

     }

     virtual void parentb_f1()

     {

         child_a = 0x70;

     }

     virtual void parentb_f2()

     {

         child_b = 0x80;

     }

};

 

CChild child, *pchild;

这个子类拥有两个父类。

1.1.1   内存布局

我们先对子类成员进行赋值,然后观察其内存变化:

     child.parenta_a = 1;

00413B9E C7 05 50 85 41 00 01 00 00 00 mov         dword ptr [child+4 (418550h)],1

     child.parenta_b = 2;

00413BA8 C7 05 54 85 41 00 02 00 00 00 mov         dword ptr [child+8 (418554h)],2

     child.parentb_a = 3;

00413BB2 C7 05 5C 85 41 00 03 00 00 00 mov         dword ptr [child+10h (41855Ch)],3

     child.parentb_b = 4;

00413BBC C7 05 60 85 41 00 04 00 00 00 mov         dword ptr [child+14h (418560h)],4

     child.child_a = 5;

00413BC6 C7 05 64 85 41 00 05 00 00 00 mov         dword ptr [child+18h (418564h)],5

     child.child_b = 6;

00413BD0 C7 05 68 85 41 00 06 00 00 00 mov         dword ptr [child+1Ch (418568h)],6

再看&child的内容:

0x0041854C  60 68 41 00 01 00 00 00 02 00 00 00 50 68 41 00  `hA.........PhA.

0x0041855C  03 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00  ................

很容易可以发现,这个子类空间中包含有两个vtbl的指针。再有就是父类和子类的数据成员。当我们把继承的顺序改为:

class CChild : public CParentB, public CParentA

再看内存:

0x0041854C  60 68 41 00 03 00 00 00 04 00 00 00 50 68 41 00  `hA.........PhA.

0x0041855C  01 00 00 00 02 00 00 00 05 00 00 00 06 00 00 00  ................

可以发现,vs对类成员的排列是按照继承的顺序而来的。

1.1.2   虚函数调用

观察子类对虚函数的调用:

     pchild = &child;

00413BDA C7 05 48 85 41 00 4C 85 41 00 mov         dword ptr [pchild (418548h)],offset child (41854Ch)

     pchild->parenta_f1();

00413BE4 A1 48 85 41 00   mov         eax,dword ptr [pchild (418548h)]

00413BE9 8B 10            mov         edx,dword ptr [eax]

00413BEB 8B F4            mov         esi,esp

00413BED 8B 0D 48 85 41 00 mov         ecx,dword ptr [pchild (418548h)]

00413BF3 8B 02            mov         eax,dword ptr [edx]

00413BF5 FF D0            call        eax 

00413BF7 3B F4            cmp         esi,esp

00413BF9 E8 74 D5 FF FF   call        @ILT+365(__RTC_CheckEsp) (411172h)

     pchild->parenta_f2();

00413BFE A1 48 85 41 00   mov         eax,dword ptr [pchild (418548h)]

00413C03 8B 10            mov         edx,dword ptr [eax]

00413C05 8B F4            mov         esi,esp

00413C07 8B 0D 48 85 41 00 mov         ecx,dword ptr [pchild (418548h)]

00413C0D 8B 42 04         mov         eax,dword ptr [edx+4]

00413C10 FF D0            call        eax 

00413C12 3B F4            cmp         esi,esp

00413C14 E8 59 D5 FF FF   call        @ILT+365(__RTC_CheckEsp) (411172h)

     pchild->parentb_f1();

00413C19 8B 0D 48 85 41 00 mov         ecx,dword ptr [pchild (418548h)]

00413C1F 83 C1 0C         add         ecx,0Ch

00413C22 A1 48 85 41 00   mov         eax,dword ptr [pchild (418548h)]

00413C27 8B 50 0C         mov         edx,dword ptr [eax+0Ch]

00413C2A 8B F4            mov         esi,esp

00413C2C 8B 02            mov         eax,dword ptr [edx]

00413C2E FF D0            call        eax 

00413C30 3B F4            cmp         esi,esp

00413C32 E8 3B D5 FF FF   call        @ILT+365(__RTC_CheckEsp) (411172h)

     pchild->parentb_f2();

00413C37 8B 0D 48 85 41 00 mov         ecx,dword ptr [pchild (418548h)]

00413C3D 83 C1 0C         add         ecx,0Ch

00413C40 A1 48 85 41 00   mov         eax,dword ptr [pchild (418548h)]

00413C45 8B 50 0C         mov         edx,dword ptr [eax+0Ch]

00413C48 8B F4            mov         esi,esp

00413C4A 8B 42 04         mov         eax,dword ptr [edx+4]

00413C4D FF D0            call        eax 

00413C4F 3B F4            cmp         esi,esp

00413C51 E8 1C D5 FF FF   call        @ILT+365(__RTC_CheckEsp) (411172h)

从上面的代码可以发现,当调用父类A的虚函数时,使用的是第一个vtbl的指针,而在调用父类B的虚函数时,使用的则是第二个vtbl的指针。

还有很重要的一点,vs是通过ecx这个寄存器来传递this指针的,上面的汇编代码表明,当调用父类A的虚函数时,this指针将指向子类的首地址,当调用父类B的虚函数时,this指针将指向存放第二个vtbl的地址,也就是父类A成员结束的位置。

跟踪可以看到,调用parenta_a时的this指针值为0x0041854c,而调用parentb_a时的this指针值为0x00418558。从前面&child的内容可以得到印证。

1.1.3   当两个父类有同名的虚函数

一个很有意思的问题,既然调用父类A的虚函数时传递的this指针和调用父类B的虚函数时传递的this指针不一样,那么假如父类A和父类B拥有同名的虚函数,vs又该如何处理呢?

试试将CParentB::parentb_f1的名称改为CParentB::parenta_f1,编译:

     pchild = &child;

00413BDA C7 05 48 85 41 00 4C 85 41 00 mov         dword ptr [pchild (418548h)],offset child (41854Ch)

     pchild->parenta_f1();

00413BE4 A1 48 85 41 00   mov         eax,dword ptr [pchild (418548h)]

00413BE9 8B 10            mov         edx,dword ptr [eax]

00413BEB 8B F4            mov         esi,esp

00413BED 8B 0D 48 85 41 00 mov         ecx,dword ptr [pchild (418548h)]

00413BF3 8B 02            mov         eax,dword ptr [edx]

00413BF5 FF D0            call        eax 

00413BF7 3B F4            cmp         esi,esp

00413BF9 E8 74 D5 FF FF   call        @ILT+365(__RTC_CheckEsp) (411172h)

显然,这个时候传递的是child的首地址。

 

 

 

 

2       参考资料

vs2008C++对象内存布局(3):加上虚函数(2009-9-10)

vs2008C++对象内存布局(2):简单继承(2009-9-9)

vs2008C++对象内存布局(1(2009-9-9)