前言
前一篇主要介绍了C++对象在普通单继承和普通多继承体系下的对象模型,没有考虑到虚继承的情况。接下来将从虚拟单继承,虚拟多继承(包括一部分虚继承和全部虚继承)等几种继承方式下分别探究下C++对象模型的特点,如未特别说明,所有虚拟继承体系下的基类实现代码与前一篇是一致的
虚拟继承
单虚拟继承的子类
简单实现代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Derive_Sin_Vir : virtual public Base{ public : Derive_Sin_Vir (int var = 40 ) : Base (var), _derive_sin_vir_var(var) {}; public : virtual void Run (void ) { std::cout << "Derive_Sin_Vir Class Run()" << std::endl; } virtual void RunDerive_Sin_Vir (void ) { std::cout << "Derive_Sin_Vir Class RunDerive_Sin_Vir()" << std::endl; } private : int _derive_sin_vir_var; };
Gcc命令的内存布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Vtable for Derive_Sin_Vir Derive_Sin_Vir::_ZTV14Derive_Sin_Vir: 11 entries 0 16 8 (int (...))(& _ZTI14Derive_Sin_Vir) 24 (int (...))Derive_Sin_Vir::RunDerive_Sin_Vir 40 0 48 18446744073709551600 56 (int (...))(& _ZTI14Derive_Sin_Vir) 72 (int (...))Base::RunBase VTT for Derive_Sin_Vir Derive_Sin_Vir::_ZTT14Derive_Sin_Vir: 2 entries 0 ((& Derive_Sin_Vir::_ZTV14Derive_Sin_Vir) + 24 )8 ((& Derive_Sin_Vir::_ZTV14Derive_Sin_Vir) + 72 )Class Derive_Sin_Vir size=32 align=8 base size=12 base align=8 Derive_Sin_Vir (0 x0x7fb40baf95b0) 0 vptridx=0 vptr=((& Derive_Sin_Vir::_ZTV14Derive_Sin_Vir) + 24 ) Base (0 x0x7fb40baf0780) 16 virtual vptridx=8 vbaseoffset=-24 vptr=((& Derive_Sin_Vir::_ZTV14Derive_Sin_Vir) + 72 )
可以看到单虚拟继承的子类的内存空间特点是新建的子类虚表内存布局+虚基类的内存布局 。子类自己的内存布局包括自己生成的虚函数表,里面包含虚函数列表和虚表以及虚基表偏移和子类的成员变量;虚基类的内存布局包括基类的虚函数表和基类的成员变量。子类的新增的虚函数或重写对的虚函数是在自己生成的虚函数表中的;而虚基类的未重写虚函数则是在继承而来的基类的虚基表中。
内存布局的代码验证 1.测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void SingleVirtualDeriveTest (void ) { Derive_Sin_Vir d; std::cout << "---------begin single virtual derive object inner memory layout test-------" << std::endl; std::cout << " object size is: " << sizeof (d) << std::endl; std::cout << " object addr is: " << &d << std::endl; std::cout << " typeid(d) is: " << typeid (d).name () << std::endl; std::cout << " object vfptr value is: " << (long *)*(long *)&d << std::endl; std::cout << " object vfptr[0] func ptr is: " << (long *)*(long *)(*(long *)&d) << std::endl; std::cout << " object vfptr[0] func invoke res: " ; pFunc pRun = (pFunc)(*(long *)(*(long *)&d)); (*pRun)(); std::cout << " object vfptr[1] addr is: " << (long *)(*((long *)(*(long *)&d) + 1 )) << std::endl; std::cout << " object vfptr[1] func invoke res: " ; pFunc pRunDerive_Sin_Vir = (pFunc)((long *)(*((long *)(*(long *)&d) + 1 ))); (*pRunDerive_Sin_Vir)(); std::cout << " object vfptr[2] vptr offset value is: " << (int )(*((long *)(*(long *)&d) + 2 )) << std::endl; std::cout << " object vfptr[3] vbase offset value is: " << (int )(*((long *)(*(long *)&d) + 3 )) << std::endl; std::cout << " object _derive_sin_vir_var addr is: " << (long *)&d + 1 << std::endl; std::cout << " object _derive_sin_vir_var value is: " << (int )*((long *)&d + 1 ) << std::endl; std::cout << " object vfptr-base addr is: " << (long *)&d + 2 << std::endl; std::cout << " object vfptr-base value is: " << (long *)(*((long *)&d + 2 )) << std::endl; std::cout << " object vfptr-base[0] func ptr is: " << (long *)(*((long *)(*((long *)&d + 2 )) + 0 )) << std::endl; std::cout << " object vfptr-base[1] func ptr is: " << (long *)(*((long *)(*((long *)&d + 2 )) + 1 )) << std::endl; std::cout << " object vfptr-base[1] func invoke res: " ; pFunc pRunBase = (pFunc)(*((long *)(*((long *)&d + 2 )) + 1 )); (*pRunBase)(); std::cout << " object _base_var addr is: " << (long *)&d + 3 << std::endl; std::cout << " object _base_var value is: " << (int )*((long *)&d + 3 ) << std::endl; std::cout << "---------end single virtual derive object inner memory layout test-------" << std::endl; }
2.运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ---------begin single virtual derive object inner memory layout test------- object size is: 32 object addr is: 0 x7ffe2ac9df60 typeid (d) is: 14 Derive_Sin_Vir object vfptr value is: 0 x5580dd6a9878 object vfptr[0] func ptr is: 0 x5580dd6a4e96 object vfptr[0] func invoke res: Derive_Sin_Vir Class Run () object vfptr[1] addr is: 0 x5580dd6a4ede object vfptr[1] func invoke res: Derive_Sin_Vir Class RunDerive_Sin_Vir () object vfptr[2] vptr offset value is: 0 object vfptr[3] vbase offset value is: -16 object _derive_sin_vir_var addr is: 0 x7ffe2ac9df68 object _derive_sin_vir_var value is: 40 object vfptr-base addr is: 0 x7ffe2ac9df70 object vfptr-base value is: 0 x5580dd6a98a8 object vfptr-base[0] func ptr is: 0 x5580dd6a4ed1 object vfptr-base[1] func ptr is: 0 x5580dd6a4d62 object vfptr-base[1] func invoke res: Base Class RunBase () object _base_var addr is: 0 x7ffe2ac9df78 object _base_var value is: 40 ---------end single virtual derive object inner memory layout test-------
3.内存布局示意图:
多虚拟继承的子类(Half)
简单实现代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Derive_Half_Mul_Vir : virtual public Base, public Base2{ public : Derive_Half_Mul_Vir (int var = 50 ) : Base (var), Base2 (var), _derive_half_mul_vir_var(var) {}; public : virtual void Run (void ) { std::cout << "Derive_Half_Mul_Vir Class Run()" << std::endl; } virtual void RunBase2 (void ) { std::cout << "Derive_Half_Mul_Vir Class RunBase2()" << std::endl; } virtual void RunDerive_Half_Mul_Vir (void ) { std::cout << "Derive_Half_Mul_Vir Class RunDerive_Half_Mul_Vir()" << std::endl; } private : int _derive_half_mul_vir_var; };
Gcc命令的内存布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Vtable for Derive_Half_Mul_Vir Derive_Half_Mul_Vir::_ZTV19Derive_Half_Mul_Vir: 12 entries 0 16 8 (int (...))(& _ZTI19Derive_Half_Mul_Vir) 24 (int (...))Derive_Half_Mul_Vir::RunBase2 40 (int (...))-16 72 (int (...))Derive_Half_Mul_Vir::_ZTv0_n24_N19Derive_Half_Mul_Vir3RunEv 88 (int
可以看到多虚拟继承的子类(Half)的内存空间特点是普通继承基类内存布局+子类自己的内存布局+虚基类的内存布局 。普通继承基类的内存布局包括普通继承而来的基类虚函数表和普通继承基类的成员变量;子类自己内存布局包括继承而来的普通基类的虚函数表和子类的成员变量。子类的虚函数则是在普通继承而来的基类 的虚函数表基础上进行新增、替换(override)、保持操作(没有override),虚基类的内存布局包括虚基类表和虚基类成员变量。
内存布局的代码验证 1.测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 void HalfMultiVirtualDeriveTest (void ) { Derive_Half_Mul_Vir d; std::cout << "---------begin half multi virtual derive object inner memory layout test-------" << std::endl; std::cout << " object size is: " << sizeof (d) << std::endl; std::cout << " object addr is: " << &d << std::endl; std::cout << " typeid(d) is: " << typeid (d).name () << std::endl; std::cout << " object vfptr value is: " << (long *)*(long *)&d << std::endl; std::cout << " object vfptr[0] func ptr is: " << (long *)*(long *)(*(long *)&d) << std::endl; std::cout << " object vfptr[0] func invoke res: " ; pFunc pRun = (pFunc)(*(long *)(*(long *)&d)); (*pRun)(); std::cout << " object vfptr[1] func ptr is: " << (long *)*((long *)(*(long *)&d) + 1 ) << std::endl; std::cout << " object vfptr[1] func invoke res: " ; pFunc pRunBase2 = (pFunc)(*((long *)(*(long *)&d) + 1 )); (*pRunBase2)(); std::cout << " object vfptr[2] func ptr is: " << (long *)*((long *)(*(long *)&d) + 2 ) << std::endl; std::cout << " object vfptr[2] func invoke res: " ; pFunc pRunDerive_Half_Mul_Vir = (pFunc)(*((long *)(*(long *)&d) + 2 )); (*pRunDerive_Half_Mul_Vir)(); std::cout << " object vfptr[3] vptr offset is: " << (int )*((long *)(*(long *)&d) + 3 ) << std::endl; std::cout << " object vfptr[4] vbase offset is: " << (int )*((long *)(*(long *)&d) + 4 ) << std::endl; std::cout << " object _base2_var addr is: " << (long *)&d + 1 << std::endl; std::cout << " object _base2_var value is: " << (int )*((long *)&d + 1 ) << std::endl; std::cout << " object _derive_half_mul_vir_var addr is: " << (long *)((int *)((long *)&d + 1 ) + 1 ) << std::endl; std::cout << " object _derive_half_mul_vir_var value is: " << *((int *)((long *)&d + 1 ) + 1 ) << std::endl; std::cout << " object vfptr-base addr is: " << (long *)&d + 2 << std::endl; std::cout << " object vfptr-base value is: " << (long *)(*((long *)&d + 2 )) << std::endl; std::cout << " object vfptr-base[0] func ptr is: " << (long *)*((long *)*((long *)&d + 2 )) << std::endl; std::cout << " object vfptr-base[1] func ptr is: " << (long *)*((long *)*((long *)&d + 2 ) + 1 ) << std::endl; std::cout << " object vfptr-base[1] func invoke res: " ; pFunc pRunBase = (pFunc)(*((long *)*((long *)&d + 2 ) + 1 )); (*pRunBase)(); std::cout << " object _base_var addr is: " << (long *)&d + 3 << std::endl; std::cout << " object _base_var value is: " << (int )*((long *)&d + 3 ) << std::endl; std::cout << "---------end half multi virtual derive object inner memory layout test-------" << std::endl; }
2.运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ---------begin half multi virtual derive object inner memory layout test------- object size is: 32 object addr is: 0 x7ffdac1071e0 typeid (d) is: 19 Derive_Half_Mul_Vir object vfptr value is: 0 x5595ce96e808 object vfptr[0] func ptr is: 0 x5595ce969f80 object vfptr[0] func invoke res: Derive_Half_Mul_Vir Class Run () object vfptr[1] func ptr is: 0 x5595ce969fc8 object vfptr[1] func invoke res: Derive_Half_Mul_Vir Class RunBase2 () object vfptr[2] func ptr is: 0 x5595ce96a004 object vfptr[2] func invoke res: Derive_Half_Mul_Vir Class RunDerive_Half_Mul_Vir () object vfptr[3] vptr offset is: 0 object vfptr[4] vbase offset is: -16 object _base2_var addr is: 0 x7ffdac1071e8 object _base2_var value is: 50 object _derive_half_mul_vir_var addr is: 0 x7ffdac1071ec object _derive_half_mul_vir_var value is: 50 object vfptr-base addr is: 0 x7ffdac1071f0 object vfptr-base value is: 0 x5595ce96e840 object vfptr-base[0] func ptr is: 0 x5595ce969fbb object vfptr-base[1] func ptr is: 0 x5595ce969d62 object vfptr-base[1] func invoke res: Base Class RunBase () object _base_var addr is: 0 x7ffdac1071f8 object _base_var value is: 50 ---------end half multi virtual derive object inner memory layout test-------
3.内存布局示意图:
多虚拟继承的子类(Both)
简单实现代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Derive_Both_Mul_Vir : virtual public Base, virtual public Base2{ public : Derive_Both_Mul_Vir (int var = 50 ) : Base (var), Base2 (var), _derive_both_mul_vir_var(var) {}; public : virtual void Run (void ) { std::cout << "Derive_Both_Mul_Vir Class Run()" << std::endl; } virtual void RunBase2 (void ) { std::cout << "Derive_Both_Mul_Vir Class RunBase2()" << std::endl; } virtual void RunDerive_Both_Mul_Vir (void ) { std::cout << "Derive_Both_Mul_Vir Class RunDerive_Both_Mul_Vir()" << std::endl; } private : int _derive_both_mul_vir_var; };
Gcc命令的内存布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Vtable for Derive_Both_Mul_Vir Derive_Both_Mul_Vir::_ZTV19Derive_Both_Mul_Vir: 19 entries 0 32 8 16 16 (int (...))(& _ZTI19Derive_Both_Mul_Vir) 32 (int (...))Derive_Both_Mul_Vir::RunBase2 48 (int (...))-16 80 (int (...))Derive_Both_Mul_Vir::_ZTv0_n24_N19Derive_Both_Mul_Vir3RunEv 96 (int (...))-32 128 (int (...))Derive_Both_Mul_Vir::_ZTv0_n24_N19Derive_Both_Mul_Vir3RunEv 144 (int
可以看到多虚拟继承的子类(Both)的内存空间特点是**子类自己的内存布局+虚基类的内存布局+虚基类2的内存布局+…**。子类自己的内存布局包括自己生成的虚函数表和子类的成员变量;虚基类的内存布局包括基类的虚函数表和基类的成员变量。子类的新增或重写的虚函数是在自己生成的虚函数表中的;而虚基类中未被重写的虚函数则是在继承而来的相应基类的虚函数表中。
内存布局的代码验证 1.测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 void BothMultiVirtualDeriveTest (void ) { Derive_Both_Mul_Vir d; std::cout << "---------begin both multi virtual derive object inner memory layout test-------" << std::endl; std::cout << " object size is: " << sizeof (d) << std::endl; std::cout << " object addr is: " << &d << std::endl; std::cout << " typeid(d) is: " << typeid (d).name () << std::endl; std::cout << " object vfptr addr is: " << (long *)*(long *)&d << std::endl; std::cout << " object vfptr[0] func ptr is: " << (long *)*(long *)(*(long *)&d) << std::endl; std::cout << " object vfptr[0] func invoke res: " ; pFunc pRun = (pFunc)(*(long *)(*(long *)&d)); (*pRun)(); std::cout << " object vfptr[1] func ptr is: " << (long *)*((long *)(*(long *)&d) + 1 ) << std::endl; std::cout << " object vfptr[1] func invoke res: " ; pFunc pRunBase2 = (pFunc)(*((long *)(*(long *)&d) + 1 )); (*pRunBase2)(); std::cout << " object vfptr[2] func ptr is: " << (long *)*((long *)(*(long *)&d) + 2 ) << std::endl; std::cout << " object vfptr[2] func invoke res: " ; pFunc pRunDeriveBothMulVir = (pFunc)(*((long *)(*(long *)&d) + 2 )); (*pRunDeriveBothMulVir)(); std::cout << " object vfptr[3] vptr offset is: " << (int )*((long *)(*(long *)&d) + 3 ) << std::endl; std::cout << " object vfptr[4] vbase offset is: " << (int )*((long *)(*(long *)&d) + 4 ) << std::endl; std::cout << " object _derive_both_mul_vir_var addr is: " << (long *)&d + 1 << std::endl; std::cout << " object _derive_both_mul_vir_var value is: " << (int )*((long *)&d + 1 ) << std::endl; std::cout << " object vfptr-base addr is: " << (long *)&d + 2 << std::endl; std::cout << " object vfptr-base value is: " << (long *)(*((long *)&d + 2 )) << std::endl; std::cout << " object vfptr-base[0] func ptr is: " << (long *)*((long *)*((long *)&d + 2 )) << std::endl; std::cout << " object vfptr-base[1] func ptr is: " << (long *)*((long *)(*((long *)&d + 2 )) + 1 ) << std::endl; std::cout << " object vfptr-base[1] func invoke res: " ; pFunc pRunBase = (pFunc)(*((long *)(*((long *)&d + 2 )) + 1 )); (*pRunBase)(); std::cout << " object _base_var addr is: " << (long *)&d + 3 << std::endl; std::cout << " object _base_var value is: " << (int )*((long *)&d + 3 ) << std::endl; std::cout << " object vfptr-base2 addr is: " << (long *)&d + 4 << std::endl; std::cout << " object vfptr-base2 value is: " << (long *)(*((long *)&d + 4 )) << std::endl; std::cout << " object vfptr-base2[0] func ptr is: " << (long *)*((long *)*((long *)&d + 4 )) << std::endl; std::cout << " object vfptr-base2[1] func ptr is: " << (long *)*((long *)(*((long *)&d + 4 )) + 1 ) << std::endl; std::cout << " object _base2_var addr is: " << (long *)&d + 5 << std::endl; std::cout << " object _base2_var value is: " << (int )*((long *)&d + 5 ) << std::endl; std::cout << "---------end both multi virtual derive object inner memory layout test-------" << std::endl; }
2.运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ---------begin both multi virtual derive object inner memory layout test------- object size is: 48 object addr is: 0 x7ffdf308a520 typeid (d) is: 19 Derive_Both_Mul_Vir object vfptr addr is: 0 x55dbd6a85760 object vfptr[0] func ptr is: 0 x55dbd6a810bc object vfptr[0] func invoke res: Derive_Both_Mul_Vir Class Run () object vfptr[1] func ptr is: 0 x55dbd6a81104 object vfptr[1] func invoke res: Derive_Both_Mul_Vir Class RunBase2 () object vfptr[2] func ptr is: 0 x55dbd6a8114c object vfptr[2] func invoke res: Derive_Both_Mul_Vir Class RunDerive_Both_Mul_Vir () object vfptr[3] vptr offset is: 0 object vfptr[4] vbase offset is: -16 object _derive_both_mul_vir_var addr is: 0 x7ffdf308a528 object _derive_both_mul_vir_var value is: 50 object vfptr-base addr is: 0 x7ffdf308a530 object vfptr-base value is: 0 x55dbd6a85798 object vfptr-base[0] func ptr is: 0 x55dbd6a810f7 object vfptr-base[1] func ptr is: 0 x55dbd6a80d62 object vfptr-base[1] func invoke res: Base Class RunBase () object _base_var addr is: 0 x7ffdf308a538 object _base_var value is: 50 object vfptr-base2 addr is: 0 x7ffdf308a540 object vfptr-base2 value is: 0 x55dbd6a857c8 object vfptr-base2[0] func ptr is: 0 x55dbd6a810f7 object vfptr-base2[1] func ptr is: 0 x55dbd6a8113f object _base2_var addr is: 0 x7ffdf308a548 object _base2_var value is: 50 ---------end both multi virtual derive object inner memory layout test-------
3.内存布局示意图:
总结
本篇主要介绍了C++类在虚拟继承下的子类对象内存特点,可以看出,子类新建的或普通继承而来的虚表中存放着子类的虚函数和表偏移值;对于纯虚继承,即所有基类均是虚拟继承(包括但不限于单虚拟继承)来说,子类对象中的新增的虚函数均是在自己生成的新虚函数表中进行操作的(若子类没有新增的虚函数或重写则不需要生成)。而对于不纯情况,子类会类似于前一篇描述的普通继承情况,使用普通继承而来的基类的虚函数表,而不会自己生成。可见,虚拟继承体系下通常情况下会存在多张虚函数表(子类普通继承而来的和某些情况下自己新生成的)。