Visual C++ 8.0对象布局的奥秘:虚函数、多继承、虚拟继承(VC直接输出内存布局)
原文:VC8_Object_Layout_Secret.html
哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleClassLayout和/d1reportAllClassLayout。看个复杂的例子吧(如下),现在假设我们想知道Derived类的对象布局,怎么办? 在Project Properties->C++->Command Line->Additional Options里面加上/d1reportSingleClassLayoutDerived吧!
class CommonBase
{
int co;
}; class Base1: virtual public CommonBase
{
public:
virtual void print1() {}
virtual void print2() {}
private:
int b1;
}; class Base2: virtual public CommonBase
{
public:
virtual void dump1() {}
virtual void dump2() {}
private:
int b2;
}; class Derived: public Base1, public Base2
{
public:
void print2() {}
void dump2() {}
private:
int d;
}; int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
F5编译之,你会惊奇地发现,Output里面有如下字样:
01 class Derived size(32):
02 +---
03 | +--- (base class Base1)
04 0 | | {vfptr}
05 4 | | {vbptr}
06 8 | | b1
07 | +---
08 | +--- (base class Base2)
09 12 | | {vfptr}
10 16 | | {vbptr}
11 20 | | b2
12 | +---
13 24 | d
14 +---
15 +--- (virtual base CommonBase)
16 28 | co
17 +---
18
19 Derived::$vftable@Base1@:
20 0 | &Base1::print1
21 1 | &Derived::print2
22
23 Derived::$vftable@Base2@:
24 0 | &Base2::dump1
25 1 | &Derived::dump2
26
27 Derived::$vbtable@Base1@:
28 0 | -4
29 1 | 24 (Derivedd(Base1+4)CommonBase)
30
31 Derived::$vbtable@Base2@:
32 0 | -4
33 1 | 12 (Derivedd(Base2+4)CommonBase)
34
35 Derived::print2 this adjustor: 0
36 Derived::dump2 this adjustor: 12
看到了吗? VC8居然输出了Derived对象的完整布局! 我们终于可以不必两眼一抹黑般的去peek/poke了….第1行表明,Derived对象总占用了32字节;其由三部分组成,分别是行3-行7、行 8-行12、行13、行28;其中前二者分别是基类Base1、Base2的布局,最后的行28为虚拟基类Common的布局。
以基类Base1部分为例,可发现其由一个虚函数表指针vftable和虚基表指针vbtable构成,先看Base1部分的vftable所指向的虚表 $vftable@Base1(行19),不难发现,其中的表项2已经被Derived::print2给override了;再来看Base2部分的 vftable所指向的虚表$vftable@Base2(行23),可发现,同样的,Base2::dump2被Derived::dump2给 override了。这不明摆着就是虚函数机制嘛,heh~
值得注意的是,这个例子同时说明,多继承场合下,其实在单一对象中是存在多个this指针的….行35-36给出了如何将Derived的this指针校正为其基类子对象this指针的偏移量,也就是说,根据行36,假设有个Derived d,那么d.dump1()实际上应该理解成通过虚表$vftable@Base2对((Base2*)(((char*)&d)+12))- >dump1()的调用….即传递给所有Base2成员函数的this指针应该是(Base2*)((char*)(&d)+12),这里可能我写得恐怖了点,意思到了就成….这不,普通继承、多继承、对象Slicing的语义都在这个布局里面了,看仔细了哈~
OK,多继承看完了,继续看虚拟基类是如何布局的。虚基Common在Derived的布局中,位于Derived本身数据成员之后的位置。Base1、 Base2中均保存了一个vbtable指针,其分别指向各自所使用的虚基表$vbtable@Base1和$vbtable@Base2,为什么要指向一个虚基表? 很简单,因为Base1、Base2有可能会同时继承多个不同的虚拟基类…..这充分体现了C++对象布局的复杂性….在每个虚基表中,保存了所继承的虚拟基类部分相对于子类部分vbtable指针的偏移值,以Base2为例,我们知道Base2的vbtable在Derived中的偏移值为16 (行10),则根据$vbtable@Base2,虚基Common部分距离Base2 vbtable指针的偏移值为12,则有虚基Common在Derived中的总偏移值为16+12。与普通多继承同理,我们在调用非虚拟的虚基成员函数时,必须将Derived的this指针调整为指向虚基部分的this指针,只有这样才能成功地访问虚基自身的数据成员和虚基的虚拟函数(通过虚基自己的 vftable,为简单起见,上例中我就没弄那么复杂了,大家可以自己玩玩,明白如何举一反三即可)
看完了上述解释,是不是感觉比啃Inside C++ Object Model来得更快更直观啊?hehe
http://cplusplus.wikidot.com/cn:vc8-object-layout-secret
Visual C++ 8.0对象布局的奥秘:虚函数、多继承、虚拟继承(VC直接输出内存布局)的更多相关文章
- Visual C++ 8.0对象布局
哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleClassLayout和/d1reportAllClassLayout ...
- c++对象内存模型【内存布局】(转)
总结:1.按1继承顺序先排布基于每个父类结构.2.该结构包括:基于该父类的虚表.该父类的虚基类表.父类的父类的成员变量.父类的成员变量.3.多重继承且连续继承时,虚函数表按继承顺序排布函数与虚函数.4 ...
- (转)C++对象的内存布局
原文地址:http://blog.csdn.net/haoel/article/details/3081328 C++ 对象的内存布局 陈皓 http://blog.csdn.net/haoel 前言 ...
- 继承虚函数浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。
本文笔者在青岛逛街的时候突然想到的...最近就有想写几篇关于继承虚函数的笔记,所以回家到之后就奋笔疾书的写出来发布了 应用sizeof函数求类巨细这个问题在很多面试,口试题中很轻易考,而涉及到类的时候 ...
- [CPP] 类的内存布局
本文可以解决下面 3 个问题: 以不同方式继承之后,类的成员变量是如何分布的? 虚函数表及虚函数表指针,在可执行文件中的位置? 单一继承.多继承.虚拟继承之后,类的虚函数表的内容是如何变化的? 在这里 ...
- C++ | 虚函数表内存布局
虚表指针 虚函数有个特点.存在虚函数的类会在类的数据成员中生成一个虚函数指针 vfptr,而vfptr 指向了一张表(简称,虚表).正是由于虚函数的这个特性,C++的多态才有了发生的可能. 其中虚函数 ...
- HotSpot源码分析之C++对象的内存布局
HotSpot采用了OOP-Klass模型来描述Java类和对象.OOP(Ordinary Object Pointer)指的是普通对象指针,而Klass用来描述对象的具体类型.为了更好理解这个模型, ...
- c++对象内存布局
这篇文章我要简单地讲解下c++对象的内存布局,虽然已经有很多很好的文章,不过通过实现发现有些地方不同的编译器还是会有差别的,希望和大家交流. 在没有用到虚函数的时候,C++的对象内存布局和c语言的st ...
- 关于Class对象、类加载机制、虚拟机运行时的内存布局的全面解析和推测
简介: 本文是对Java的类加载机制,Class对象,反射原理等相关概念的理解.验证和Java虚拟机中内存布局的一些推测.本文重点讲述了如何理解Class对象以及Class对象的作用. 欢迎探讨,如有 ...
随机推荐
- [RxJS] Combination operator: combineLatest
While merge is an OR-style combination operator, combineLatest is an AND-style combination operator. ...
- Instruction (hdu 5083)
Instruction Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- /proc/sysrq-trigger
立即重启计算机 echo "b" > /proc/sysrq-trigger 立即关闭计算机 echo "o" > /proc/ ...
- 理解ArcGIS Javascript Viewer Widget及编程模型
一个ArcGIS Javascript Viewer for JavaScript Widget是一组可以共享.迁移及部署到JavaScript View程序中的的文本文件.通常,一个程序员如果要开发 ...
- JSP-标准动作标记
JSP标准动作标记 在客户请求JSP页面时,JSP动作利用XML语法格式的标记来控制Servlet引擎的行为.利用JSP动作可以动态地插入文件.重用JavaBean组件.把用户重定向到另外的页面.为J ...
- jQuery循环给某个ID赋值
1.id名为sl的input框循环赋值 $("input[id=sl]").each(function(){alert(this.value) })
- 关于ajaxfileupload.js一些问题和上传图片就立即显示图片功能
ajaxfileupload.js是上传文件的一个插件,最近碰到的一个问题是在谷歌浏览器上传文件之后,原文本框中的文件名称消失,网上搜了好长时间也没有十分满意的答案.无刷新上传文件我想到的只有ajax ...
- JS cookie 读写操作
/*** ** 功能: cookie操作对象 ***/ var cookies = { /*** ** 功能: 写入cookie操作 ** 参数: name cookie名称 ** value coo ...
- cocos2dx 的Hello world的简单分析
Node之间的关系: 场景AppDelegate.cpp又要由导演去调用然后进行表演: // create a scene. it's an autorelease object auto scene ...
- HTML5 WebAudioAPI简介(一)
一.常用对象 1.AudioContext对象 AudioContext是一个专门用于音频处理的接口,并且原理是讲AudioContext创建出来的各种节点(AudioNode)相互连接,音频数据流经 ...