这几天翻箱底将去年买的《深度探索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。
现在你觉得class A的大小应该是多少呢?一个虚基类子对象只会在派生类中存在一份实体,不管他在继承体系中出现多少次,所以公用一个1byte的classX实体,再加上class Y和class Z这样就有9bytes,如果有对齐的话就是12bytes但是vs2010中省略了那1byte所以就不存在对齐就直接是8bytes。谜底终于揭开了!!!