一直以来在各个论坛上都不时的见过一些关于类大小的讨论,尤其是当涉及到虚继承时,类的大小就变得更加扑朔迷离,每看完一个帖子都觉得自己有所收获,但当下次遇到类似的帖子时却怎么也想不起自己以前对此问题的记忆了,于是乎,干脆勤快些一劳永逸地把他们记录下来。纯属个人理解,难免有错,我会定期更新这篇文章,修改其中的错误之处。本文仅供参考!也希望诸位高手多多纠正其中的错误。谢谢!
关于类的大小问题,有些是和编译器有关的。很多技术取决于编译器的具体实现。所以编译器不同,得到的结果有时会有所出入。下面所有的测试不加声明的话都是基于VS2005。
为使问题更加清晰,下面的讨论将不考虑对齐问题。
#pragma pack(1)
class a {
char i;
int ii;
};
class b: virtual public a{
char c;
};
class c: virtual public a{};
class d: public b, public c{};
#pragma pack()
int main(int argc, char *argv[])
{
cout << "a size is " << sizeof(a) << endl;
cout << "b size is " << sizeof(b) << endl;
cout << "b size is " << sizeof(c) << endl;
cout << "d size is " << sizeof(d) << endl;
system("PAUSE");
return EXIT_SUCCESS;
}
输出结果是:
5
10
9
14
理解以上问题的关键在于了解编译器对于虚函数以及虚基类的处理方法。
记得我第一次了解涉及到虚函数的类的内存布局是在Lipperman的
<Inside C++ Object Model> 中,中文版是由侯捷翻译的。书中讲到微软使用的所谓虚函数表,用来存储虚函数指针,这个估计大家都了解。其实虚函数表还有另一个用途就是用来存储虚基类的指针。指向虚基类的指针是以负数为下标存贮在虚表中的。
b的大小a的大小加上一个虚函数表指针的大小和一个b中成员变量的大小,也就是10。
c同理。
d的内存布局大致如下:
/**********************************************************/
现在看来我上面的理解好像是错误的20080403
/**********************************************************/
看一下代码:
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()
{};
};
int main()
{
cout < <sizeof(A) < <endl;
cout < <sizeof(B) < <endl;
cout < <sizeof(C) < <endl;
}
在VC 中的运行结果为:
8
20
32
在GCC中为
8,16,24
看来虚基类的存放方法有待探讨。
初步结论如下:
如果类A定义了虚函数,而B继承自A(非虚继承)。那么B中只包含一个虚函数表(其中存放了A中的虚函数和B中新定义的虚函数)。而当虚继承被采用时,VC会增加一个新的指针指向虚基类表(表中存放B的所有虚基类地址),而且B中新定义的虚函数也会被放在一个新的虚函数表中。因此这种情况下会增加至少两个指针的空间。而GCC则有所不同,只新增了一个指针的空间,可能是以上分析中的一个。
多继承的情况以后再说。
评论