快乐虾
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;
这个子类拥有两个父类。
我们先对子类成员进行赋值,然后观察其内存变化:
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)
分享到:
相关推荐
VC8.0上多重继承的内存布局
1* 类如何布局? 2* 成员变量如何访问? 3* 成员函数如何访问? 4* 所谓的“调整块”(adjuster thunk)是... * 单继承、多重继承、虚继承 * 虚函数调用 * 强制转换到基类,或者强制转换到虚基类 * 异常处理
-) 对象的影响因素 简而言之,我们一个类可能会有如下的影响因素: 1)成员变量2)虚函数(产生虚函数表)3)单一继承(只继承于一个类)4)多重继承(继承多个类
在多重继承之下,指向Member Functions的指针 “指向Member Functions之指针”的效率 4.5 Inline Functions 形式对数(Formal Arguments) 局部变量(Local Variables) 第5章 构造、解构、拷贝 语意学(Semantics ...
在多重继承之下,指向Member Functions的指针 “指向Member Functions之指针”的效率 4.5 Inline Functions 形式对数(Formal Arguments) 局部变量(Local Variables) 第5章 构造、解构、拷贝 语意学(Semantics ...
2 多重继承 3 虚拟继承 1 单一继承 (1)派生类完全拥有基类的内存布局,并保证其完整性。 派生类可以看作是完整的基类的Object再加上派生类自己的Object。如果基类中没有虚成员函数,那么派生类与具有相同功能...
本文我们将阐释GCC编译器针对多重继承和虚拟继承下的对象内存布局。尽管在理想的使用环境中,一个C++程序员并不需要了解这些编译器内部实现细节,实际上,编译器针对多重继承(特别是虚拟继承)的各种实现细节对于我们...
C++的经典之作,弄清C++对象的内存布局,对于多重继承和COM学习有很大的帮助
本书重点:探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序...
3.4对象布局 3.5类 3.5.1用存取控制来修改stash 3.5.2用存取控制来修改stack 3.6句柄类(handleclasses) 3.6.1可见的实现部分 3.6.2减少重复编译 3.7小结 3.8练习 第4章 初始化与清除 4.1用构造函数确保初始化 4.2...
内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...
内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...
内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...
内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...
内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...
内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...
内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...
内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...