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

本文深入探讨了C++中多重继承的内存布局及虚函数调用机制,包括不同继承顺序对内存布局的影响,以及当基类中有同名虚函数时的调用行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

快乐虾

http://blog.youkuaiyun.com/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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值