内存布局的原则是:成员变量按其被声明的顺序排列,按具体实现所规定的对齐原则在内存地址上对齐。
除非为了实现虚函数和虚继承引入的隐藏成员变量外,C++类实例的大小完全取决于一个类及其基类的成员变量!成员函数基本上不影响类实例的大小。实际上只有成员变量才占用类实例的空间。静态成员存放在程序的数据段中,不在类实例中,因此不占用类内存空间。
单继承
C++提供继承的目的是在不同的类型之间提取共性。派生类要保留基类的所有属性和行为,因此每个派生类的实例都包含了一份完整的基类实例数据。基类的数据最好放在派生类的数据之前,这样派生类的指针就不需要计算偏移量就能获取到基类的指针。在单继承类层次下,每一个新的派生类都简单地把自己的成员变量添加到基类的成员变量之后。对象的指针指向同一地址。
多重继承
多重继承同样拷贝了每个基类的所有数据。但多重继承下,内嵌的两个·基类的对象指针不可能全都与派生类对象指针相同。派生类与基类对象指针之间的偏移量会造成少量的调用开销。
虚继承
若派生类继承的多个基类中,各自继承于同一个基类,为了保证派生类中只含有一个最终基类的拷贝,需要在派生类继承的基类声明中,实现“虚继承”,即指定基类时声明为“virtual”,表明“共享继承”。如下:
struct Employee { ... };
struct Manager : virtual Employee { ... };
struct Worker : virtual Employee { ... };
struct MiddleManager : Manager, Worker { ... };
Worker、Manager类都继承Employee类,这样MiddleManager类中就会有两份Employee类的拷贝,浪费内存,若其中一份修改了,也会造成数据不一致的问题出现。
使用虚继承,相比于单继承和多继承有更大的实现开销和调用开销。
在单继承和多重继承的情况下,内嵌的基类实例地址比起派生类实例地址来,要么地址相同(单继承,以及多重继承的最靠左基类) ,要么地址相差一个固定偏移量(多重继承的非最靠左基类) 。 然而,当虚继承时,一般说来,派生类地址和其虚基类地址之间的偏移量是不固定的,因为如果这个派生类又被进一步继承的话,最终派生类会把共享的虚基类实例数据放到一个与上一层派生类不同的偏移量处。
浙公网安备 33010602011771号