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

快乐虾

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

[email protected]

本文适用于

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)

你可能感兴趣的:(C++,c,XP,Blog,C#)