C++中類對象的內存佈局以及虛函數表生成

類對象內存佈局計算

l 空類、單一繼承的空類、多重繼承的空類所佔空間大小爲:1(字節,下同);

l 一個類中,虛函數本身、成員函數(包括靜態與非靜態)和靜態數據成員都是不佔用類對象的存儲空間的;

l 因此一個對象的大小≥所有非靜態成員大小的總和;

l 當類中聲明瞭虛函數(不管是1個還是多個),那麼在實例化對象時,編譯器會自動在對象裏安插一個指針vPtr指向虛函數表VTable;

l 虛承繼的情況:由於涉及到虛函數表和虛基表,會同時增加一個(多重虛繼承下對應多個)vfPtr指針指向虛函數表vfTable和一個vbPtr指針指向虛基表vbTable,這兩者所佔的空間大小爲:8(或8乘以多繼承時父類的個數);

l 在考慮以上內容所佔空間的大小時,還要注意編譯器下的“補齊”padding的影響,即編譯器會插入多餘的字節補齊;

l 類對象的大小=各非靜態數據成員(包括父類的非靜態數據成員但都不包括所有的成員函數)的總和+ vfptr指針(多繼承下可能不止一個)+vbptr指針(多繼承下可能不止一個)+編譯器額外增加的字節。

虛函數表

C++多態的實現和Java類似,都是利用虛函數表(Java叫方法表),不同的是,Java中,不用virtual修飾,函數自帶virtual屬性(指的是那些非靜態函數)。之前的文章《從JVM角度看Java多態》中,也簡單分析了Java多態的實現機制。C++和Java是類似的。下面我們直接分析吧。

1)對於單繼承

這裏寫圖片描述

Child類重寫了Parent的f(),GrandChild類重寫了Child類的f()和g_child();

GrandChild對象的內存佈局爲:

這裏寫圖片描述

可以看到:

l 虛函數表在最前面的位置。

l 成員變量根據其繼承和聲明順序依次放在後面。

l 在單一的繼承中,被overwrite的虛函數在虛函數表中得到了更新。

2)對於多繼承

這裏寫圖片描述

Derive對象的內存佈局

這裏寫圖片描述

可以看到:

l 每個父類都有自己的虛表。

l 子類的成員函數被放到了第一個父類的表中。

l 內存佈局中,其父類佈局依次按聲明順序排列。

l 每個父類的虛表中的f()函數都被overwrite成了子類的f()。這樣做就是爲了解決不同的父類類型的指針指向同一個子類實例,而能夠調用到實際的函數。

3)對於菱形繼承問題

這裏寫圖片描述

D對象的內存佈局

這裏寫圖片描述

可以看到

我們可以看見,最頂端的父類B其成員變量存在於B1和B2中,並被D給繼承下去了。而在D中,其有B1和B2的實例,於是B的成員在D的實例中存在兩份,一份是B1繼承而來的,另一份是B2繼承而來的。所以,容易產生二義性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章