快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
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;
这个子类拥有两个父类。
我们先对子类成员进行赋值,然后观察其内存变化:
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对类成员的排列是按照继承的顺序而来的。
观察子类对虚函数的调用:
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的内容可以得到印证。
一个很有意思的问题,既然调用父类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的首地址。
vs2008下C++对象内存布局(3):加上虚函数(2009-9-10)
vs2008下C++对象内存布局(2):简单继承(2009-9-9)
vs2008下C++对象内存布局(1)(2009-9-9)