以下均以Visual Studio 2015 /d1 reportSingleClass输出结果分析
0 基础内存分布
0.0 所有non-static成员数据均分布在Object内;
0.1 所有non-static、static、virtual成员方法均分布在Object外;
0.2 当存在vfptr时,vfptr的第0项为typeinfo信息;
0.3 需要时,将会被填充字节进行内存对齐;
1 加上继承
1.0 单继承
1.0.0 若基类存在虚方法,则Object包含vfptr(virtual function table pointer),此vfptr为基类的vfptr,反之则无;
1.0.1 若派生类复写了基类的虚方法,则vfptr中基类虚方法将被覆盖;
2.0 多继承
1.0.0 若多个基类存在虚方法,则Object包含多个vfptr分别对应各个基类;其中,派生类以继承列表中第一个基类的vfptr作为自己的vfptr(此处以Vistual Studio为准);
1.0.1 1.0.0中所描述,若派生类复写了多个基类中同名的虚方法,则对应各基类的虚方法都将被定向到派生类所复写的方法;Visual Studio依然以第一个基类的vfptr作为派生类的vfptr,其他基类vfptr中该同名方法后将以goto标记到派生类vfptr中的复写方法;如下:
1> class CDerived size(12):
1> +---
1> 0 | +--- (base class CBase)
1> 0 | | {vfptr}
1> 4 | | m_base
1> | +---
1> 8 | +--- (base class COther)
1> 8 | | {vfptr}
1> | +---
1> +---
1>
1> CDerived::$vftable@CBase@:
1> | &CDerived_meta
1> | 0
1> 0 | &CDerived::display
1>
1> CDerived::$vftable@COther@:
1> | -8
1> 0 | &thunk: this-=8; goto CDerived::display
1>
1> CDerived::display this adjustor: 0
1.0.2 基类在内存中的顺序与继承列表一致;
3.0 virtual继承
3.0.0 单独virtual 继承
A. 派生类Object此时不再包含虚基类的任何成员,仅用vbptr(virtual base table pointer)指向一个虚基类表,其中存放虚基类的地址;
B. 若此时,派生类此时复写了虚基类方法,则复写的方法仍然覆盖到基类的vfptr中;
C. 若此时,派生类拥有自己独立的虚方法,则Object将拥有独立于第一个基类的vfptr
3.0.1 virtual + 普通继承
A. 此时,派生类将拥有普通基类子对象、vbptr、自身的成员;
B. 此时,派生类若无自己独立的虚方法,则以继承列表中第一个普通基类的vfptr作为自己的vfptr(看派生类的typeinfo信息在哪个vfptr中);
C. 由B所述,若派生类此时拥有自己独立的虚方法时,仍以继承列表中第一个普通基类的vfptr作为自身的vfptr,而不再如3.0.0:C中所述将拥有自己独立的vfptr;如:
1> class CDerived size(16):
1> +---
1> 0 | +--- (base class COther)
1> 0 | | {vfptr}
1> | +---
1> 4 | {vbptr}
1> +---
1> +--- (virtual base CBase)
1> 8 | {vfptr}
1> 12 | m_base
1> +---
1>
1> CDerived::$vftable@COther@:
1> | &CDerived_meta
1> | 0
1> 0 | &COther::display
1> 1 | &CDerived::displayA
1>
1> CDerived::$vbtable@:
1> 0 | -4
1> 1 | 4 (CDerivedd(CDerived+4)CBase)
1>
1> CDerived::$vftable@CBase@:
1> | -8
1> 0 | &CBase::display
1>
1> CDerived::displayA this adjustor: 0
1> vbi: class offset o.vbptr o.vbte fVtorDisp
1> CBase 8 4 4 0
D. 若派生类复写了,普通基类与虚基类相同签名的虚方法,则以第一个普通基类的vfptr为准,并将虚基类的该方法定向到该处;如:
class CDerived size(16):
1> +---
1> 0 | +--- (base class COther)
1> 0 | | {vfptr}
1> | +---
1> 4 | {vbptr}
1> +---
1> +--- (virtual base CBase)
1> 8 | {vfptr}
1> 12 | m_base
1> +---
1>
1> CDerived::$vftable@COther@:
1> | &CDerived_meta
1> | 0
1> 0 | &CDerived::display
1>
1> CDerived::$vbtable@:
1> 0 | -4
1> 1 | 4 (CDerivedd(CDerived+4)CBase)
1>
1> CDerived::$vftable@CBase@:
1> | -8
1> 0 | &thunk: this-=8; goto CDerived::display
1>
1> CDerived::display this adjustor: 0
1> vbi: class offset o.vbptr o.vbte fVtorDisp
1> CBase 8 4 4 0
3.0.2 多virtual继承
A. 派生类将拥有自身成员以及一个vbptr;
B. Virutal base table pointer 中的基类在内存中按继承列表顺序;
C. 若此时,派生类拥有自己独立的虚方法,则派生类将拥有自己独立的vfptr;
D. 若此时,派生类复写了基类相同的签名方法,则派生类将根据继承列表顺序为准,复写虚基类的vfptr,其他虚基类的vfptr将以goto重定向到该处;
E. 若派生类同时满足C、D描述中的条件,则各条件均遵守其结果:独立虚方法,将使派生类拥有自己的vfptr;而复写的方法仍按D所述。如:
1> class CDerived size(20):
1> +---
1> 0 | {vfptr}
1> 4 | {vbptr}
1> +---
1> +--- (virtual base CBase)
1> 8 | {vfptr}
1> 12 | m_base
1> +---
1> +--- (virtual base COther)
1> 16 | {vfptr}
1> +---
1>
1> CDerived::$vftable@CDerived@:
1> | &CDerived_meta
1> | 0
1> 0 | &CDerived::forTest
1>
1> CDerived::$vbtable@:
1> 0 | -4
1> 1 | 4 (CDerivedd(CDerived+4)CBase)
1> 2 | 12 (CDerivedd(CDerived+4)COther)
1>
1> CDerived::$vftable@CBase@:
1> | -8
1> 0 | &CDerived::display
1>
1> CDerived::$vftable@COther@:
1> | -16
1> 0 | &thunk: this-=8; goto CDerived::display
1>
1> CDerived::forTest this adjustor: 0
1> CDerived::display this adjustor: 8
1> vbi: class offset o.vbptr o.vbte fVtorDisp
1> CBase 8 4 4 0
1> COther 16 4 8 0