Just a note
在Project Properties->C++->Command Line->Additional Options里面加上/d1reportSingleClassLayoutDerived编译时,可输出类Derived的内存布局。
非虚拟单继承
1>class Derived size(12):1> +---1> | +--- (base class Base)1> 0 | | {vfptr}1> 4 | | iBase1> | +---1> 8 | iDerived1> +---1>Derived::$vftable@:1> | &Derived_meta1> | 01> 0 | &Derived::Func21> 1 | &Derived::Func5非虚拟多继承
1>class Derived size(28):1> +---1> | +--- (base class Base)1> 0 | | {vfptr}1> 4 | | iBase1> | +---1> | +--- (base class Base1)1> 8 | | {vfptr}1>12 | | iBase11> | +---1> | +--- (base class Base2)1>16 | | {vfptr}1>20 | | iBase21> | +---1>24 | iDerived1> +---1>Derived::$vftable@Base@:1> | &Derived_meta1> | 01> 0 | &Derived::Func21> 1 | &Derived::Func51>Derived::$vftable@Base1@:1> | -81> 0 | &Base1::Func11> 1 | &thunk: this-=8; goto Derived::Func21> 2 | &Derived::Func31>Derived::$vftable@Base2@:1> | -161> 0 | &thunk: this-=16; goto Derived::Func21> 1 | &thunk: this-=8; goto Derived::Func31> 2 | &Base2::Func4虚拟单继承
1>class Derived size(20):1> +---1> 0 | {vfptr}1> 4 | {vbptr}1> 8 | DerivedValue1> +---1> +--- (virtual base Base)1>12 | {vfptr}1>16 | BaseValue1> +---1>Derived::$vftable@Derived@:1> | &Derived_meta1> | 01> 0 | &Derived::Func41>Derived::$vbtable@:1> 0 | -41> 1 | 8 (Derivedd(Derived+4)Base)1>Derived::$vftable@Base@:1> | -121> 0 | &Derived::Func2虚拟多继承
1>class Derived size(32):1> +---1> | +--- (base class Base2)1> 0 | | {vfptr}1> 4 | | iBase21> | +---1> 8 | {vbptr}1>12 | iDerived1> +---1> +--- (virtual base Base)1>16 | {vfptr}1>20 | iBase1> +---1> +--- (virtual base Base1)1>24 | {vfptr}1>28 | iBase11> +---1>Derived::$vftable@Base2@:1> | &Derived_meta1> | 01> 0 | &Derived::Func21> 1 | &Derived::Func31> 2 | &Base2::Func41> 3 | &Derived::Func51>Derived::$vbtable@:1> 0 | -81> 1 | 8 (Derivedd(Derived+8)Base)1> 2 | 16 (Derivedd(Derived+8)Base1)1>Derived::$vftable@Base@:1> | -161> 0 | &thunk: this-=16; goto Derived::Func21>Derived::$vftable@Base1@:1> | -241> 0 | &Base1::Func11> 1 | &thunk: this-=24; goto Derived::Func21> 2 | &thunk: this-=24; goto Derived::Func3
菱形虚拟继承
Derived d;
Derived* pd = &d;
Top* pt = &d;
cout<<(
1>class Derived size(32):1> +---1> | +--- (base class Left)1> 0 | | {vfptr}1> 4 | | {vbptr}1> 8 | | LeftValue1> | +---1> | +--- (base class Right)1>12 | | {vbptr}1>16 | | RightValue1> | +---1>20 | DerivedValue1> +---1> +--- (virtual base Top)1>24 | {vfptr}1>28 | TopValue1> +---1>Derived::$vftable@Left@:1> | &Derived_meta1> | 01> 0 | &Left::Func21> 1 | &Derived::Func41>Derived::$vbtable@Left@:1> 0 | -41> 1 | 20 (Derivedd(Left+4)Top)1>Derived::$vbtable@Right@:1> 0 | 01> 1 | 12 (Derivedd(Right+0)Top)1>Derived::$vftable@Top@:1> | -241> 0 | &Derived::Func1如上代码的输出结果表示虚基类指针的地址比派生类指针地址多了24,符合上面列出的对象内存布局。当通过对象访问基类成员时,无论是否为虚拟继承,都可以再编译时期获得对象内存布局,直接计算出偏移量访问。当通过指针访问基类成员时,如果是非虚拟继承,直接计算偏移量访问;如果是虚拟继承,需要先计算出虚基类表指针的偏移地址,再根据指针找到虚基类表,取虚基类表中该项的内容,根据此内容计算虚基类对象的偏移地址,再根据成员在虚基类对象中的偏移地址访问。 所以一般说来,当从派生类中访问虚基类成员时,应该先强制转化派生类指针为虚基类指针,然后一直使用虚基类指针来访问虚基类成员变量。这样做,可以避免每次都要计算虚基类地址的开销。 虚函数表不一定在派生类对象的首地址,例如当派生类从基类虚拟继承时,派生类如果没有定义新的虚函数,那么在派生类的对象空间中,首先分布的是虚基类表,然后是派生类成员,最后是虚基类对象,虚函数表在虚基类对象开始位置。
1>class Derived size(12):1> +---1> 0 | {vbptr}1> 4 | i1> +---1> +--- (virtual base Base)1> 8 | {vfptr}1> +---1>Derived::$vbtable@:1> 0 | 01> 1 | 8 (Derivedd(Derived+0)Base)1>Derived::$vftable@:1> | -81> 0 | &Derived::fun
Powered by: C++博客 Copyright © 大胖