以下均以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