这几天翻箱底将去年买的《深度探索C++对象模型》这本NB的书拿出来看看,The Semantics of Data这一章中发现了一个过去一直没有想到的一个问题,问题如下
输出下面class的大小:
class X{}; class Y : public virtual X{}; class Z : public virtual X{}; class A : public Y, public Z{};
继承关系如下图:
这是可能大家会觉得他们的大小都应该是0,因为他们中没有任何一个有明显的数据,只表示了继承关系。但是至少也认为class x应该是0吧,他什么都没有。结果却让你想不到,我在vs2010环境下测试的大小是:(不同编译器可能这个大小是不一样)
cout<<"sizeof X: " <<sizeof X<<endl <<"sizeof Y: " <<sizeof Y<<endl <<"sizeof Z: " <<sizeof Z<<endl <<"sizeof A: " <<sizeof A<<endl;
很奇怪吧,为什么是这个结果呢。一个空的class事实上并不是空,它有一个隐藏的1 byte,这个是编译器安插进去的char,这样可以保证定义的对象在内存中的大小是的,这个地方你可以自己测试下,比如:
X xa,xb; if (&xa == &xb) cout<<"is equal"<<endl; else cout<<"not equal"<<endl;
但是让人搞不懂的是Y、Z的大小。主要大小受三个因素的影响:
● 语言本身所造成的额外负担,当语言支持虚基类的时候,导致一个额外的负担,这个一般都是一个虚表指针。里面存储的是虚基类子对象的地址,是偏移量。
● 编译器对于特殊情况所提供的优化处理,因为class X有1 byte的大小,这样出现在了class Y和class Z身上。这个主要视编译器而定,比如某些存在这个1byte但是有些编译器将他忽略了(因为已经用虚指针了所以这个1byte可以不用作为内存中的一个定位)。
● Alignment的限制,是所谓的对齐操作,比如你现在占用5bytes编译器为了更有效率地在内存中存取将其对齐为8byte。
下面说明在vs2010中的模型,因为有了虚指针后所以1byte不用了,所以class Y和class Z的大小是4bytes,如下图:
现在你觉得class A的大小应该是多少呢?一个虚基类子对象只会在派生类中存在一份实体,不管他在继承体系中出现多少次,所以公用一个1byte的classX实体,再加上 class Y和class Z这样有9bytes,如果有对齐的话是12bytes但是vs2010中省略了那1byte所以不存在对齐直接是8bytes。谜底终于揭开了!