关于虚拟继承的内存分布参考如下文章:
在项目属性中的命令行加入以下语句,编译的时候显示了类的内存布局
测试程序(运行环境VS2008或VC6.0):
#include <iostream>
using namespace std;
class A
{
char k[3];
public:
virtual void aa(){};
};
class B :public virtual A
{
char j[3];
public:
virtual void bb(){};
};
class C :public virtual B
{
char i[3];
public:
virtual void cc(){};
};
void main()
{
cout<<"sizeof(A):"<<sizeof(A)<<endl;
cout<<"sizeof(B):"<<sizeof(B)<<endl;
cout<<"sizeof(C):"<<sizeof(C)<<endl;
}
输出结果:
这个结果与《程序员面试宝典》的答案有出入,可能是编译器不同的原因吧
B的内存占用为
虚函数表指针---->4字节
指向父类的虚类指针---->4字节
j[3]---->4字节(内存对齐)
A---->8字节
C类的占用空间可以此类推4*3+20=32但是注意,
虚拟继承爷爷永远在爸爸前面!(A在B前面)
改为直接继承的方式:
#include <iostream>
using namespace std;
class A
{
char k[3];
public:
virtual void aa(){};
};
class B :public A //去掉了virtual
{
char j[3];
public:
virtual void bb(){};
};
class C :public B //去掉了virtual
{
char i[3];
public:
virtual void cc(){};
};
void main()
{
cout<<"sizeof(A):"<<sizeof(A)<<endl;
cout<<"sizeof(B):"<<sizeof(B)<<endl;
cout<<"sizeof(C):"<<sizeof(C)<<endl;
}
由此看出普通继承仅仅继承了数据,每个子类都有一个虚函数表指针
将虚函数改为普通函数,但是采用虚拟继承方式:
#include <iostream>
using namespace std;
class A
{
char k[3];
public:
void aa(){}; //去掉了virtual
};
class B :public virtual A
{
char j[3];
public:
void bb(){}; //去掉了virtual
};
class C :public virtual B
{
char i[3];
public:
void cc(){}; //去掉了virtual
};
void main()
{
cout<<"sizeof(A):"<<sizeof(A)<<endl;
cout<<"sizeof(B):"<<sizeof(B)<<endl;
cout<<"sizeof(C):"<<sizeof(C)<<endl;
}
查看B的内存布局:
由此看出指向父类A虚表的指针vbptr占4字节
j[3]内存对齐后占4字节,k[3]占3字节,总共11字节
查看C的内存布局:
虚表指针4字节
i[3] 3字节+对齐2字节+A的数据k[3] 3字节 = 8字节
虚表指针4字节+B的数据j[3]内存对齐4字节
总共20字节
B类的基类A的数据在最后没有进行数据对齐,而C类的祖父A在前面进行了字节对齐且方式较为特殊,而且它的父类B进行了字节对齐,这个现象属于编译器的特性,略显特殊