從記憶體布局看C 虛繼承的實現原理

2021-08-04 13:47:50 字數 2350 閱讀 6675

今天講的是虛基類和虛繼承,我們就先用上面介紹的命令提示工具檢視一下普通多繼承子類的記憶體布局,可以跟後文虛繼承子類的記憶體布局情況加以比較。

我們新建乙個名叫normalinheritance的cpp檔案,輸入一下內容。

/** 

普通繼承(沒有使用虛基類)

*/// 基類a

class a

; class b : public a

; class c : public a

; class d : public b, public c

;

上面是乙個簡單的多繼承例子,我們啟動visual studio命令提示功能,切換到normalinheritance.cpp檔案所在目錄,輸入一下命令:

c1 normalinheritance.cpp /d1reportsingleclasslayoutd

我們可以看到class d的記憶體布局如下

從類d的記憶體布局可以看到a派生出b和c,b和c中分別包含a的成員。再由b和c派生出d,此時d包含了b和c的成員。這樣d中就總共出現了2個a成員。大家注意到左邊的幾個數字,這幾個數字表明了d中各成員在d中排列的起始位址,d中的五個成員變數(b::dataa、datab、c::dataa、datac、datad)各占用4個位元組,sizeof(d) = 20。

為了跟後文加以比較,我們再來看看b和c的記憶體布局:

上面我們看到了普通多繼承子類的記憶體分布情況,下面我們進入主題,來看看典型的菱形虛繼承子類的記憶體分布情況。

我們新建乙個名叫virtualinheritance的cpp檔案,輸入一下內容:

/** 

虛繼承(虛基類)

*/

#include // 基類a

class a

; class b : virtual public a

; class c : virtual public a

; class d : public b, public c

;

virtualinheritance.cpp和normalinheritance.cpp的不同點在與c和c繼承a時使用了virtual關鍵字,也就是虛繼承。同樣,我們看看b、c、d類的記憶體布局情況:

我們可以看到,菱形繼承體系中的子類在記憶體布局上和普通多繼承體系中的子類類有很大的不一樣。對於類b和c,sizeof的值變成了12,除了包含類a的成員變數dataa外還多了乙個指標vbptr,類d除了繼承b、c各自的成員變數datab、dataa和自己的成員變數外,還有兩個分別屬於b、c的指標。

那麼類d物件的記憶體布局就變成如下的樣子:

vbptr:繼承自父類b中的指標

int datab:繼承自父類b的成員變數

vbptr:繼承自父類c的指標

int datac:繼承自父類c的成員變數

int datad:d自己的成員變數

int a:繼承自父類a的成員變數

顯然,虛繼承之所以能夠實現在多重派生子類中只儲存乙份共有基類的拷貝,關鍵在於vbptr指標。那vbptr到底指的是什麼?又是如何實現虛繼承的呢?其實上面的類d記憶體布局圖中已經給出答案:

實際上,vbptr指的是虛基類表指標(virtual base table pointer),該指標指向了乙個虛表(virtual table),虛表中記錄了vbptr與本類的偏移位址;第二項是vbptr到共有基類元素之間的偏移量。在這個例子中,類b中的vbptr指向了虛表d::$vbtable@b@,虛表表明公共基類a的成員變數dataa距離類b開始處的位移為20,這樣就找到了成員變數dataa,而虛繼承也不用像普通多繼承那樣維持著公共基類的兩份同樣的拷貝,節省了儲存空間。

c 虛繼承物件的記憶體布局

好了,我們從最基礎的的討論起。當c 支援virtual base class 時,就會多了一些額外負擔,當class 中內含乙個或多個virtual base class subobject時,將分成兩個部分,乙個不變區域性和乙個共享區域性。最初的方案是為每乙個虛基類安插乙個指標指向這個虛基類,其缺...

虛繼承及繼承的記憶體布局

1.為什麼需要虛繼承 如下圖所示如果訪問der fun or der m nvalue就會帶來二義性,無法確定是呼叫base1的還是base2的,所以為了解決多重繼承情況下成員訪問的二義性,引入了虛繼承機制。一般繼承 虛繼承 2.虛繼承實現 在虛繼承下,der通過共享虛基類superbase來避免二...

C 虛繼承中的物件記憶體布局

鑽石型虛擬繼承 虛繼承是為了解決多繼承中的資料冗餘而出現的。列印虛函式表 void printfmove int vbptr 列印偏移量 cout void test int main 程式執行結果 物件在記憶體中的布局 所以,有以下結論 在虛繼承時,類中會自動加乙個指標 vbptr 該變數指向乙個...