C 物件的記憶體布局

2021-06-07 02:44:51 字數 2736 閱讀 2972

一篇寫的比較好的部落格 

這篇文章中主要想說以下幾個問題

1 如何通過物件獲得虛函式表中虛函式的位址

2 分幾種情況討論記憶體布局

1》單一繼承

2》多重繼承

3》重複繼承

4》鑽石虛擬繼承(為了解決重複繼承中出現問題而產生的虛擬繼承) 1 

虛函式主要是通過一張虛函式的位址表來實現的,簡稱v-table,在有虛函式的例項中這個表被分配在這個例項的記憶體中,

當我們用乙個父類指標來操作乙個子類的時候,這張虛函式表就顯的非常重要,告訴我們應該呼叫哪個函式。當然這麼重要的表

的初始化是在物件的建構函式中,這樣才能反映物件真實的意願,指標才能呼叫到正確的函式。

c++編譯器保證虛函式表指標存在於物件例項中最前面的位置。其他的編譯器也有不同的安排

這樣我們可以通過物件來獲取虛函式表的位址,然後便利其中的函式指標。

&b物件位址

(int*)&b

虛指標的位址(存放虛函式表的位址的指標)

*(int*)&b

虛函式表的位址中存放的內容,及虛函式表的位址

(int*)*(int*)&b

第乙個虛函式的位址

1 單一繼承

程式就不寫了,可以看那篇部落格的

繼承關係描述:

grandchild 的記憶體布局:

結論:1 虛函式在最前面

2 成員變數的根據其繼承和宣告順序依次放在後面

3 在單一繼承中,被overwrite的虛函式在虛函式表中得到了更新。函式的順序按照繼承宣告的順序依次存放。

2 多重繼承

我們可以看到: 1

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

)子類的成員函式被放到了第乙個父類的表中。 3

)記憶體布局中,其父類布局依次按宣告順序排列。 4

)每個父類的虛表中的f()函式都被overwrite成了子類的f()。這樣做就是為了解決不同的父類型別的指標指向同乙個子類例項,而能夠呼叫到實際的函式。

3  重複繼承

所謂重複繼承,也就是某個基類被間接地重複繼承了多次。

我們可以看見,最頂端的父類b其成員變數存在於b1和b2中,並被d給繼承下去了。而在d中,其有b1和b2的例項,於是b的成員在d的例項中存在兩份,乙份是b1繼承而來的,另乙份是b2繼承而來的。所以,如果我們使用以下語句,則會產生二義性編譯錯誤:

d d;

d.ib = 0;

//二義性錯誤

d.b1::ib = 1;

//正確

d.b2::ib = 2;

//正確

注意,上面例程中的最後兩條語句訪問的是兩個變數。雖然我們消除了二義性的編譯錯誤,但b類在d中還是有兩個例項,這種繼承造成了資料的重複,我們叫這種繼承為重複繼承。重複的基類資料成員可能並不是我們想要的。所以,c++引入了虛基類的概念。

4 鑽石型虛擬繼承

圖說明:

總結:1 把b這個超類放在了最後,vc++有乙個null分隔符把b和b1和b2的布局分開,圖中忘記了畫

2 順序

先是b1 ,然後是b2 ,接著是d ,而b這個超類的例項都放在最後的位置。

3 對於不同的編譯器,函式處理的方式可能不同,有的將d::f這個函式,b,b1,b2這三個vptr中都有,而vc++的只將它放在

b的vptr中。編譯器可以計算偏移量得出想要的結果

C 物件的記憶體布局

記憶體布局是屬於較深層次的知識,很多問題往深了講都是不清楚記憶體布局的原理。最近讀到一本書,裡面講了一部分c 物件的記憶體布局,讓我對很多以前的問題都豁然開朗了。書上篇幅較大,我加上自己的理解總結了下。分為三部分 簡單物件,單繼承,多繼承 非靜態成員變數和虛函式是決定類大小的唯一兩個因素 非靜態成員...

C 物件的記憶體布局

主要有三個因素對物件的記憶體布局有較大影響 類成員型別 static成員變數,virtual成員函式 繼承方式 記憶體對齊。以下分別詳細說明了具體的影響。一 static與virtual對記憶體布局的影響 物件的記憶體分布與類的成員有關,static成員變數與非static成員變數會造成不同的記憶體...

c 物件的記憶體布局

以下均為linux64位編譯器上實驗資料,指標大小為8位元組 1.空類 class n n n sizeof n 等於1編譯器會安插乙個char位元組以保證其每個例項都有唯一的位址 2.無虛函式無繼承類 class a a a sizeof a 等於8class和struct一樣會滿足記憶體對齊,a...