(二)C++类的内存大小计算

计算一个类对象的大小规律:

    1、空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,用于标记对象地址);

    2、一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;

    3、因此一个对象的大小≥所有非静态成员大小的总和(包括动态分配的变量...);

    4、当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;

    5、虚继承的情况:虚继承的实现是通过一个虚基类指针列表;由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr指针指向虚函数表vfTable和一个vbPtr指针指向虚基表vbTable,这两者所占的空间大 小为:8(或8乘以多继承时父类的个数);

    6、在考虑以上内容所占空间的大小时,还要注意编译器下的“补齐”padding的影响,即编译器会插入多余的字节补齐(对齐规则见下面);

    7、类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。

一、非虚单继承

计算顺序:父类大小(虚函数指针+数据成员)(二)C++类的内存大小计算自己数据成员大小

底层原理可以理解为:

1、子类继承了父类的虚函数表和虚函数表指针。

2、若子类重写了父类的虚函数,则更新虚函数表中虚函数的地址为重写后函数地址。

(二)C++类的内存大小计算

二、非虚类多继承

计算顺序:从左到右计算父类大小(父类计算方法:虚指针大小(二)C++类的内存大小计算自己数据成员大小(二)C++类的内存大小计算自己数据成员

底层原理可理解为:

1、子类继承了各个父类的虚函数表和虚函数指针(每个父类都有单独的虚函数指针和虚函数表)

2、子类将自己的虚函数信息更新到第一个父类的虚函数表中

(二)C++类的内存大小计算

三、虚继承的钻石继承:

计算顺序:从左到右计算直接父类大小(父类大小计算方法:虚函数指针+虚基类指针+成员数据)(二)C++类的内存大小计算自己的数据(二)C++类的内存大小计算最顶层父类的虚虚函数指针(前提是虚函数)(二)C++类的内存大小计算最顶层父类数据

(二)C++类的内存大小计算

 

综上总结:

1、单个类大小 = 虚函数表指针(如果存在虚函数)+数据成员大小

2、非虚继承(包括:单继承、多继承)场景:

子类继承第一个父类的虚函数表和虚函数指针,并更新第一个父类的虚函数表。

子类大小 = 各父类大小(从左(二)C++类的内存大小计算右,父类计算包括虚函数指针) + 子类自己成员函数大小

3、虚继承场景:

1)中间派生类大小 = 虚函数表指针+虚基类表指针+数据成员大小

2)子类大小 = 各父类大小(从左(二)C++类的内存大小计算右,父类计算包括虚函数指针、虚基表指针) + 子类自己成员函数大小+最顶层父类大小

 


C++内存对其规则:

1、第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行(只要存放位置是这个值得整数倍)。

2、在数据成员完成各自对齐之后,类(结构或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

  很明显#pragma pack(n)作为一个预编译指令用来设置多少个字节对齐的。值得注意的是,n的缺省数值是按照编译器自身设置,一般为8,合法的数值分别是1、2、4、8、16。

  即编译器只会按照1、2、4、8、16的方式分割内存。若n为其他值,是无效的。