#include<iostream>
using namespace std;
class A
{
};
class B
{
char ch;
virtual void func0() { }
};
class C
{
char ch1;
char ch2;
virtual void func() { }
virtual void func1() { }
};
class D: public A, public C
{
int d;
virtual void func() { }
virtual void func1() { }
};
class E: public B, public C
{
int e;
virtual void func0() { }
virtual void func1() { }
};
int main(void)
{
cout<<"A="<<sizeof(A)<<endl; //result=1
cout<<"B="<<sizeof(B)<<endl; //result=16
cout<<"C="<<sizeof(C)<<endl; //result=16
cout<<"D="<<sizeof(D)<<endl; //result=16
cout<<"E="<<sizeof(E)<<endl; //result=32
return 0;
}
在GCC编译器下进行编译,有如下结果
结果分析:
1.A为空类,所以大小为1 ;
2.B的大小为char数据成员大小+vptr指针大小。由于字节对齐,大小为8+8=16 ;
3.C的大小为两个char数据成员大小+vptr指针大小。由于字节对齐,大小为8+8=16 ;
4.D为多继承派生类,由于D有数据成员,所以继承空类A时,空类A的大小1字节并没有计入当中,D继承C,此情况D只需要一个vptr指针,所以大小为数据成员加一个指针大小。由于字节对齐,大小为8+8=16 ;
5.E为多继承派生类,此情况为我们上面所讲的多重继承,含虚函数覆盖的情况。此时大小计算为数据成员的大小+2个基类虚函数表指针大小 ,考虑字节对齐,继承顺序B在先,B(8 + 1),然后是C(8+1+1),由于字节对齐,B得与C中最大值对齐,因此B+7变成16,再+C(10),得26,最后+E的其它成员+1,因为要整体对于最大值(8)对齐,因此补齐得32。
在vc编译器下进行编译,有如下结果

结果分析:
4.D为多继承派生类,由于D有数据成员,所以继承空类A时,空类A的大小1字节并没有计入当中,D继承C,此情况D只需要一个vptr指针,所以大小为数据成员加一个指针大小。这里和GCC最大的区别就是D和C不能合并在一起单独进行内存对齐的计算,需要分开进行计算,所以由于字节对齐,大小为8+1+1+6+4+4=24 ;
5.E为多继承派生类,含虚函数覆盖的情况。此时大小计算为数据成员的大小+2个基类虚函数表指针大小 ,同样犹豫每个类需要单独进行内存对齐的计算,所以有8+1+7+8+1+1+6+4+4=40
二.虚继承的情况
对虚继承层次的对象的内存布局,在不同编译器实现有所区别。
在这里,首先要说的是在gcc编译器下,虚继承大小的计算。它在gcc下实现比较简单,不管是否虚继承,GCC都是将虚表指针在整个继承关系中共享的,不共享的是指向虚基类的指针。
class A {
int a;
virtual void myfuncA(){}
};
class B:virtual public A{
virtual void myfunB(){}
};
class C:virtual public A{
virtual void myfunC(){}
};
class D:public B,public C{
virtual void myfunD(){}
};
sizeof(A)=16,sizeof(B)=24,sizeof(C)=24,sizeof(D)=48;
A的大小为int大小加上虚表指针大小;
B,C中由于是虚继承,因此大小为int大小加指向虚基类的指针的大小。B,C虽然加入了自己的虚函数,但是虚表指针是和基类共享的,因此不会有自己的虚表指针,他们两个共用虚基类A的虚表指针。D由于B,C都是虚继承,其大小等于B+C)。
在VC编译器下,有如下结果图:

在VC编译器下,由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr指针指向虚函数表vfTable和一个vbPtr指针指向虚基表vbTable,这两者所占的空间大小为:16(或16乘以多继承时父类的个数),vc下虚表指针不会共享;
sizeof(C)=32,其中内存分布如下图所示:
- class C size(32):
- +---
- | | {vfptr}
- | | {vbptr}
- | | {vptr}
- | +---
- | +--- (virtual base class A)
- | a
sizeof(D)=48,其中内存分布如下图所示:
- class D size(48):
- +---
- | +--- (base class B)
- | | {vfptr}
- | | {vbptr}
- | +---
- | +--- (base class C)
- | | {vfptr}
- | | {vbptr}
- | +---
- | |{vptr}
- +---
- +--- (virtual base A)
- | d
- +---
本文探讨了C++中类的内存布局,包括空类、含有虚函数的类、多继承派生类在GCC和VC编译器下的大小差异。详细分析了虚函数表指针(vptr)和字节对齐规则如何影响类的大小。在虚继承的情况下,GCC和VC编译器的不同处理方式也进行了比较。通过对实例的分析,揭示了不同编译器在内存管理和多继承处理上的策略差异。
316

被折叠的 条评论
为什么被折叠?



