C++虚函数表分析
? ? ? ? ?在<<反C++>>文中提到的 VC6.0 上的編譯器支持一個d1reportAllClassLayout的開關, 能夠輸出全部對象的內存布局信息,?我自己經常使用vs2005來開發,所以這里我就以vs2005為例講怎么設置d1reportAllClassLayout。
? ? ? ? 右鍵項目屬性(Properties)—》配制屬性(Configuration Properties)—》C/C++--》命令行(Command Line)的框里輸入/d1reportAllClassLayout,就可以看類的對象布局。例如以下圖:(當然也能夠使用命令行:cl –d1reportSingleClassLayout[classname] test.cpp)
測試代碼:
class CBase { public:virtual void fun(void) {}private:int m_valuable; }; 輸出結果: class CBase size(8):+---0 | {vfptr}4 | m_valuable+--- CBase::$vftable@:| &CBase_meta| 00 | &CBase::fun CBase::fun this adjustor: 0 CDerived:非virtual繼承CBase,持有一個virtual重寫方法和一個virtual新方法。一個成員變量 class CDerived: public CBase { public:void fun(void) { }virtual void vfun(void) { }public:int m_derived; }; 編譯后輸出結果: class CDerived size(12):+---| +--- (base class CBase)0 | | {vfptr}4 | | m_valuable| +---8 | m_derived+--- CDerived::$vftable@:| &CDerived_meta| 00 | &CDerived::fun1 | &CDerived::vfun CDerived::fun this adjustor: 0 CDerived::vfun this adjustor: 0 當中vftable@CBase的CBase::fun項在這里更新為CDerived::fun,同一時候添加了一項CDerived::vfun。 CDerived2:virtual繼承CBase,持有一個virtual重寫方法和一個virtual新方法,一個成員變量。因此結構為vftable@自身,vbtable@自身,member@自身,以及CBase結構。 總結: class CDerived2: virtual public CBase { public:void fun(void) { }virtual void vfun(void) { }public:int m_derived; }; 結果輸出: class CDerived2 size(20):+---0 | {vfptr}4 | {vbptr}8 | m_derived+---+--- (virtual base CBase) 12 | {vfptr} 16 | m_valuable+--- CDerived2::$vftable@CDerived2@:| &CDerived2_meta| 00 | &CDerived2::vfun CDerived2::$vbtable@:0 | -41 | 8 (CDerived2d(CDerived2+4)CBase) CDerived2::$vftable@CBase@:| -120 | &CDerived2::fun CDerived2::fun this adjustor: 12 CDerived2::vfun this adjustor: 0 vbi: class offset o.vbptr o.vbte fVtorDispCBase 12 4 4 0 當中vftable@自身僅僅有一項:CDerived2::vfun(),vbtable@自身僅僅有一項:它virtual繼承的父類CBase。而vftable@CBase原來的CBase::fun更新為CDerived2::fun。CDerived3:virtual繼承CBase,因此結構為vbtable@自身,member@自身。CBase結構 class CDerived3: virtual public CBase { public:void fun(void) { }public:int m_derived3; }; 結果輸出: class CDerived3 size(16):+---0 | {vbptr}4 | m_derived3+---+--- (virtual base CBase)8 | {vfptr} 12 | m_valuable+--- CDerived3::$vbtable@:0 | 01 | 8 (CDerived3d(CDerived3+0)CBase) CDerived3::$vftable@:| -80 | &CDerived3::fun CDerived3::fun this adjustor: 8 vbi: class offset o.vbptr o.vbte fVtorDispCBase 8 0 4 0 CGDerived:繼承CDerived2、CDerived3 class CGDerived: public CDerived2, public CDerived3 { public:void vfun() { }virtual void vgfun() { }public:int m_gd; }; 輸出: class CGDerived size(32):+---| +--- (base class CDerived2)0 | | {vfptr}4 | | {vbptr}8 | | m_derived| +---| +--- (base class CDerived3) 12 | | {vbptr} 16 | | m_derived3| +--- 20 | m_gd+---+--- (virtual base CBase) 24 | {vfptr} 28 | m_valuable+--- CGDerived::$vftable@CDerived2@:| &CGDerived_meta| 00 | &CGDerived::vfun1 | &CGDerived::vgfun CGDerived::$vbtable@CDerived2@:0 | -41 | 20 (CGDerivedd(CDerived2+4)CBase) CGDerived::$vbtable@CDerived3@:0 | 01 | 12 (CGDerivedd(CDerived3+0)CBase) CGDerived::$vftable@CBase@:| -240 | &thunk: this-=12; goto CDerived2::fun CGDerived::vfun this adjustor: 0 CGDerived::vgfun this adjustor: 0 vbi: class offset o.vbptr o.vbte fVtorDispCBase 24 4 4 0 因此首先是CDerived2的結構和CDerived3的結構,自己的新virtual方法vgfun則加入在最左父類CDerived2的虛函數表中。然后是自己的成員。最后。CDerived2和CDerived3的父類CBase結構也帶入當中。
它的fun默認指向CGDerived的最左父類CDerived2::fun。
CGG:繼承CGDerived class CGG: public CGDerived { public: int m_kc; }; 輸出: class CGG size(36): +--- | +--- (base class CGDerived) | | +--- (base class CDerived2) 0 | | | {vfptr} 4 | | | {vbptr} 8 | | | m_derived | | +--- | | +--- (base class CDerived3) 12 | | | {vbptr} 16 | | | m_derived3 | | +--- 20 | | m_gd | +--- 24 | m_kc +--- +--- (virtual base CBase) 28 | {vfptr} 32 | m_valuable +--- CGG::$vftable@CDerived2@: | &CGG_meta | 0 0 | &CGDerived::vfun 1 | &CGDerived::vgfun CGG::$vbtable@CDerived2@: 0 | -4 1 | 24 (CGGd(CDerived2+4)CBase) CGG::$vbtable@CDerived3@: 0 | 0 1 | 16 (CGGd(CDerived3+0)CBase) CGG::$vftable@CBase@: | -28 0 | &thunk: this-=16; goto CDerived2::fun vbi: class offset o.vbptr o.vbte fVtorDisp CBase 28 4 4 0 添加一個變量,基本僅僅是把CGDerived的結構再套一層。最后加上自己的成員變量。
vbtable所指向的父類結構依舊在最后。 空類 class CBase2 { }; 輸出: class CBase2 size(1): +--- +--- CD2:virtual繼承則一定會創建vbtable,用vbptr指針指向,因此size為4 class CD2: virtual public CBase2 { }; 輸出: class CD2 size(4): +--- 0 | {vbptr} +--- +--- (virtual base CBase2) +--- CD2::$vbtable@: 0 | 0 1 | 4 (CD2d(CD2+0)CBase2) vbi: class offset o.vbptr o.vbte fVtorDisp CBase2 4 0 4 0 CE:非virtual繼承CD2和CDerived2。于是依照類的聲明順序,先帶入CDerived2的結構,再帶入CD2的結構(而不是按繼承順序) class CE: public CD2, public CDerived2 { }; 輸出: class CE size(24): +--- | +--- (base class CDerived2) 0 | | {vfptr} 4 | | {vbptr} 8 | | m_derived | +--- | +--- (base class CD2) 12 | | {vbptr} | +--- +--- +--- (virtual base CBase2) +--- +--- (virtual base CBase) 16 | {vfptr} 20 | m_valuable +--- CE::$vftable@CDerived2@: | &CE_meta | 0 0 | &CDerived2::vfun CE::$vbtable@CD2@: 0 | 0 1 | 4 (CEd(CD2+0)CBase2) 2 | 4 (CEd(CE+12)CBase) CE::$vbtable@CDerived2@: 0 | -4 1 | 12 (CEd(CDerived2+4)CBase) CE::$vftable@CBase@: | -16 0 | &thunk: this-=4; goto CDerived2::fun vbi: class offset o.vbptr o.vbte fVtorDisp CBase2 16 12 4 0 CBase 16 12 8 0 CF:virtual繼承CBase和CBase2,vbtable此時的項便有兩個。此時依照繼承的順序,而不是依照類的聲明順序 class CF: virtual public CBase2, virtual public CBase { }; 輸出: class CF size(12): +--- 0 | {vbptr} +--- +--- (virtual base CBase2) +--- +--- (virtual base CBase) 4 | {vfptr} 8 | m_valuable +--- CF::$vbtable@: 0 | 0 1 | 4 (CFd(CF+0)CBase2) 2 | 4 (CFd(CF+0)CBase) CF::$vftable@: | -4 0 | &CBase::fun vbi: class offset o.vbptr o.vbte fVtorDisp CBase2 4 0 4 0 CBase 4 0 8 0
總結:
?繼承方式:非virtual繼承:導入各個父類的結構(依照父類聲明的順序,從上到下),自身member在最后
? ? ? ? ?重寫virtual方法:更新該方法最早定義的類的vftable
? ? ? ??新的virtual方法:在最左父類的vftable添加
?繼承方式:有virtual繼承:在自身member后添加virtual父類的結構(依照子類繼承的順序從左到右)。同一時候在最前面添加vbtable(假設沒有的話),添加一項指向父類結構
? ? ? ? ?重寫virtual方法:更新該方法的最早定義的類的vftable
? ? ? ? ?新的virtual方法:在自身最前面添加vftable(假設沒有的話),在自己的vftable添加
版權聲明:本文博客原創文章,博客,未經同意,不得轉載。
總結
 
                            
                        - 上一篇: 读《代码整洁之道》前四章浅显印象 和 我
- 下一篇: 荒野大镖客2夏尔马值得入手吗 夏尔马属性
