#include
using namespace std;
class A
{
char a[3];
public:
virtual void aa(){};
};
class B :public virtual A
{
char b[3];
public:
virtual void bb(){};
};
class C :public virtual B
{
char c[3];
public:
virtual void cc(){};
};
int main()
{
cout<<"sizeof(A)="<<sizeof(A)<<endl;
cout<<"sizeof(B)="<<sizeof(B)<<endl;
cout<<"sizeof(C)="<<sizeof(C)<<endl;
return 0;
}
不同的编译器不一样,估计作者用的是gcc,我在线验证了下也是这个结果
但是你得到别的也不奇怪。
不要在二进制/字节的层面纠结太多,C++在这一层面没有标准,怎么实现全看编译器。
不过确实也有些东西是几乎所有编译器都遵循的,比如数据结构的对齐与填充(Datastructure Alignment and Datastructure Padding)。
简单点说就是,为了更高效的管理内存,编译器基本都会采用对齐规则将较短的数据成员通过填充一些没有意义的Byte完成对齐。这样可以简化内存管理,提高效率。现在的硬盘都是地址对齐的,比如4KB对齐,都是同样的道理。所以你要是哪天发现一个文件夹实际占用空间比文件实际大小大很多也不要惊讶,很可能你里面有一堆一两KB甚至几百Byte的文件,这些文件在文件系统里都会分别占用4KB空间。
单独的char类型变量至少会被填充成short大小,也可能是int大小,取决于应用场景甚至可能更大,char数组可能会被填充成指针大小的整数倍,比如在64位操作系统里,char[1]直到chr[8]大小可能都是一致的。
对于虚函数,类的实体会有一个指针指向虚函数表,从而确定运行时执行哪个版本的虚函数。虚继承也会有一个虚基类表,从而保证基类成员的合理继承。对于gcc,这两个表是共用的,因此只需要一个额外的指针指向这个表就可以了,于是从题主的代码和答案来看,书的作者用的应该是32位的gcc或者采用同样策略的其他编译器。因为这种情况下,char a[3]被填充到一个指针,即4个Byte大小。然后A有一个额外的指针指向自己的虚函数表,共8Byte;B有A的全部成员,外加自己的成员和虚函数/基类表,共16Byte,同理,C占用24Byte
如果题主用的是64的gcc,那么答案可能是A占16字节,B32个,C48个
如果题主用的是VC,那么,虚函数表和虚基类表分别存在不同的位置,需要两个额外的指针,因此64位情况下估计是16、40、64. 而32位情况下是8、20、32