C++ 對象模型 筆記一

以下均以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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章