`
xiaoer_1982
  • 浏览: 1816476 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

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

阅读更多

快乐虾

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)

分享到:
评论

相关推荐

    VC8.0上多重继承的内存布局

    VC8.0上多重继承的内存布局

    c++继承中的内存布局

    1* 类如何布局? 2* 成员变量如何访问? 3* 成员函数如何访问? 4* 所谓的“调整块”(adjuster thunk)是... * 单继承、多重继承、虚继承 * 虚函数调用 * 强制转换到基类,或者强制转换到虚基类 * 异常处理

    C++ 对象的内存布局(上)1

    -) 对象的影响因素 简而言之,我们一个类可能会有如下的影响因素: 1)成员变量2)虚函数(产生虚函数表)3)单一继承(只继承于一个类)4)多重继承(继承多个类

    深度探索模C++对象模型PDF

    在多重继承之下,指向Member Functions的指针 “指向Member Functions之指针”的效率 4.5 Inline Functions 形式对数(Formal Arguments) 局部变量(Local Variables) 第5章 构造、解构、拷贝 语意学(Semantics ...

    深度探索C++对象模型 超清版

    在多重继承之下,指向Member Functions的指针 “指向Member Functions之指针”的效率 4.5 Inline Functions 形式对数(Formal Arguments) 局部变量(Local Variables) 第5章 构造、解构、拷贝 语意学(Semantics ...

    浅谈C++中派生类对象的内存布局

     2 多重继承  3 虚拟继承 1 单一继承 (1)派生类完全拥有基类的内存布局,并保证其完整性。 派生类可以看作是完整的基类的Object再加上派生类自己的Object。如果基类中没有虚成员函数,那么派生类与具有相同功能...

    多重继承及虚继承中对象内存的分布

    本文我们将阐释GCC编译器针对多重继承和虚拟继承下的对象内存布局。尽管在理想的使用环境中,一个C++程序员并不需要了解这些编译器内部实现细节,实际上,编译器针对多重继承(特别是虚拟继承)的各种实现细节对于我们...

    Inside C++ mode

    C++的经典之作,弄清C++对象的内存布局,对于多重继承和COM学习有很大的帮助

    《深度探索C++对象模型》(Stanley B·Lippman[美] 著,侯捷 译)

    本书重点:探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序...

    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...

    c++学习经典图书:C++ 编程思想(全).rar

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

    C++编程思想.pdf

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

    C++ 语言 详细教程电子版

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

    C++编程思想(中文版) chm

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

    C++编程思想 pdf

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

    C++编程思想 (作者学习C++亲身体会及多年教学经验)

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

    C++编程思想(中文版)

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

    C++编程思想1-5 清晰PDF

    内容涉及对象的演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流介绍、常量、内联函数、命名控制、引用和拷贝构造函数、运算符重载、动态对象创建、继承和组合、多态和虚函数、模板和包容器...

Global site tag (gtag.js) - Google Analytics