1.普通空类(空类,单一继承的空类,多重继承的空类)的大小:一般编译器会插入一个char(1byte)作为标识,使不同类获得独一无二的地址。(只有空类占一个char,虽然他的基类也是空类但是现在的编译器一般不会为他分配一个char,因为这样没有意义)
2.含有虚基类的空类的大小:受三个因素影响
①语言本身所造成的额外负担:
继承自虚基类的类编译器一般会插入一个指针,指针指向虚基类表或者虚基类的子对象。虚基类表中存放的是虚基类的子对象的地址或者是其偏移位置。 虚基类表中第一个值是该 虚基类表到派生类起始地址的偏移;之后的值依次是该派生类的虚基类到该表位置的地址偏移(虚基类对象的地址与派生类的“虚基类表指针”之间的偏移量)。
②编译器对于特殊情况所提供的优化处理:
传统的编译器会将空虚基类子对象插入的1byte也放在其派生类上(位置放在派生类的固定部分的尾部)。而现在的编译器将空虚基类视为派生类对象最开头的一部分(这一部分没有占用内存),而这一部分也被视为成员,所以也不需要下面提到的alignment。
③Alignment的限制:
类似于C++Primer中提到的整形提升,编译器会根据不同计算机将聚合的结构体的内存提升至方便机器处理的位数(使总线bus的运输量达到最高)(32位提升到32位,8byte)。
下面是测试代码(MSVC++2017)
#include <iostream> using namespace std; struct A{}; struct B :virtual A{}; struct C:virtual A{}; struct D : C,B{}; struct E:B{}; struct F:E,B{}; int main() { A a; B b; C c; D d; E e; F f; cout << sizeof(a) << endl; cout << sizeof(b) << endl; cout << sizeof(c) << endl; cout << sizeof(d) << endl; cout << sizeof(e) << endl; cout << sizeof(f) << endl; }
结果是
1 4 4 8 4 8
将E的继承方式改为虚继承后
struct E:virtual B{};
结果为
1 4 4 8 8 12
可见,只要继承方式中有一个虚继承就会增加一个指向虚继承表的指针并且这个这个指针将作为类的一部分继承给派生类。