首先我们从一个有趣的问题引入:
class A { };
当我们运行 :cout<<"sizeof(A)"<<endl;会惊奇的发现,j结果不是0,而是1;
为啥会是这样呢?其实在《深度探索C++对象模型》一本书中,有详细的解读,我在这只想说说自己的理解:首先class A 是一个类型,是用来实例化对象的,所以它就必须有具体的空间,使系统能按照它的格式分配内存。
如果 A a; 当然sizeof(a)的结果也是1。这个是因为它是以一个对象存在的,我们必有有一个空间(地址)能够让我们的程序找到它。
所以让我们再返回书中描述,在这种情况下,编译器为我们自动插入一个 char 以满足能够占用内存的效果。
class A{ int a; char c; };
class B :public virtual A { };
class C :public virtual A { };
class D:public B ,public C { };
下面是调试代码:
#include<iostream>
using namespace std;
class A{
int a;
char c;
};
class B :public virtual A { };
class C :public virtual A { };
class D:public B ,public C { };
int main(void)
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
cout<<sizeof(D)<<endl;
return 0;
}
上面结果 : 8 12 12 16
C++在类的数据成员布局上有几个因素:
1: 语言本身所造成的负担。
当语言支持virtual base class 时,就会产生一些类似vptr的额外指针,会是类的内存空间扩大。上面的例子就是 B C 没有添加任何数据成员,只是virtual的继承了A,他们的内存就扩大了4 (一个指针的容量)。理解这块,可以和vptr 联系起来,类似。
2:Alignment (内存对对齐)。
这个类似于C语言中struct中的内存对齐,及异质数据组合,编译器会为了CUP读去数据的效率而做一些优化。即 字节对齐。
有关内存对齐的问题可以参考这篇文 :
http://blog.csdn.net/trageday_motata/article/details/20637509
由于内存对齐的原因,A的值为8. 在继承过程中,派生类的大小是在基类的基础上增加自己添加的空间,B 和C添加了一个类似vptr的指针,所以是12.
那么,D为啥是16呢,按照添加的原理,不是应该是12+12=24 为啥是16呢,这就是c++中虚继承的缘故,虚继承就是为这种,多重派生类中可能会存在多个基类的副本而提出的。即12+12-8=16 减去一个A的副本就是本来D的空间大小。
另外,我们如果把calss A 改一下:
class A{
int a;
char c;
virtual void fun()=0;
};
这是的结果会是:12 ,因为有了虚函数,编译器就会为每一个类的对象分配一个vptr,用来指向自己的虚函数表 virtual table