關於vtordisp知多少?
首先從產生「vtordisp」問題的那個例子開始。
class base
};
class der:virtual public base
virtual void fun(){}
};
der物件模型如下:
1> class der size(20):
1> +---
1> 0 |
1> 4 | der
1> +---
1> 8 | (vtordisp for vbase base)
1> +--- (virtual base base)
1> 12 |
1> 16 | base
1> +---
1>
1> der::$vbtable@:
1> 0 | 0
1> 1 | 12 (derd(der+0)base)
1>
1> der::$vftable@:
1> | -12
1> 0 | &(vtordisp) der::fun
1>
1> der::fun this adjustor: 12
1>
1> vbi: class offset o.vbptr o.vbte fvtordisp
1> base 12 0 4 1
我們發現物件的8位元組偏移處,使用了4個位元組儲存了虛基類base的vtordisp。在我的前一篇部落格內,我們並未涉及這個內容。首先,我從查閱一下vtordisp的msdn的解釋
。msdn給出的解釋是:虛繼承中派生類重寫了基類的虛函式,並且在建構函式或者析構函式中使用指向基類的指標呼叫了該函式,編譯器會為虛基類新增vtordisp域。
然而,經過vs2010的測試,我們發現上述示例**便會產生vtordisp欄位!條件是。
1. 派生類重寫了虛基類的虛函式。
2. 派生類定義了建構函式或者析構函式。
這兩個條件缺一不可,這個結論與這裡的描述
是一致的。
但是,到目前為止,我們只是確定了vtordisp的產生條件而已。它究竟為什麼存在物件的模型中,物件如何使用它,我們仍一無所知!
按照前邊的資料內容,這個欄位和編譯選項/vd相關。/vd被稱為構造置換(具體什麼意思,我也不太清楚,慚愧!),它所解決的問題是:由於對類的虛擬基的置換與對其派生類的置換之間有差異,可能會向虛函式傳遞錯誤的this 指標。該解決方案向類的各個虛擬基提供稱作vtordisp 欄位的單個構造置換調整。但是如何構造產生錯誤this指標的測試用例,請恕作者才疏學淺不能給出,也希望看到此文的大牛們給出測試用例。
另外,編譯器還提供了預編譯命令關閉vtordisp欄位的產生。
#pragma vtordisp()
在我們剛才**的前段關閉該字段的產生,事實證明也不會產生預期的錯誤,這的確匪夷所思,園子內另一篇部落格下
中找到了另外一些線索。按照它的描述,這個字段一直儲存為0。為了證明該猜測,我們用der構造乙個物件der,並檢視該物件的記憶體內容。
參考物件模型,該物件vtordisp的位置的確儲存的是0。
曾經我遇到過乙個虛擬繼承的例項,在物件的初始化過程中會修改vtordisp欄位,但是每次在初始化結束前都會把vtordisp減去乙個常量使得它的最終結果為0。而且沒有出現任何訪問該字段的彙編指令!(既然不訪問,為何浪費指令設定它的值呢?)因此,這也讓我懷疑編譯器設計vtordisp的合理性。
無論如何,我們發現對編譯器產生的vtordisp欄位了解的是太少了。單純依靠**動作猜測該欄位存在的含義可行性十分有限,希望對此內容清楚的園友能給出合理的解釋。
C 虛繼承和虛繼承
虛繼承是在多繼承中為了解決衝突而技術。學術一點來說,是指乙個指定的基類,在繼承體系結構中,將其成員資料例項共享給也從這個基類直接或間接派生的其他類。虛繼承非常有用,可以避免多繼承的歧義和多重拷貝。考慮有如下繼承結構。b和c繼承a,d多繼承b c,我們看以下 class a class b publi...
虛函式 虛繼承 C
關於虛表,我們就要用到乙個關鍵字 virtual,可以修飾函式,也可以修飾類。類的成員函式被virtual修飾之後,就成為了虛函式 修飾類,主要是虛繼承。在此之前,我們首先要了解乙個概念 物件模型,也就是說,乙個基類形成之後,裡面的成員是怎麼存放的,當派生類繼承基類之後,派生類的成員是怎麼存放的。我...
C 多重繼承 虛繼承
c 中的多繼承,建構函式處理並沒有問題,物件構造的時候按照繼承中宣告的順序呼叫多個父類的建構函式,析構函式同樣遵守單繼承中的原則。二意性問題 如果多基類中存在同名成員,會產生二意性的問題 比如,root1類中宣告doany 介面,root2類中也宣告了doany 介面,child多承繼root1和r...