基類和派生類記憶體詳解

2021-07-16 14:01:41 字數 2131 閱讀 6576

昨天室友問了我乙個c++基類和派生類的記憶體方面的問題,由於當時不能給出確切的答案,所以今天在vs上面乙個個測試了一遍,今記錄下來。

問題歸結為如下**:

class a

{public:

a():a(0)

{ cout<

{ b b;   

a *p=&b;

p->fun();

p->vfun();

p->a=2;   //通過p改變a中a的值

couta::a的值(基類的成員變數a),而b物件的a值依然沒有變(b::a==5,a::a=2);

物件b在記憶體中的記憶體分布為:

如上圖所示,指標p只會取出b中被方框框起來的部分,所以,指標p訪問的a並「不是」b的a。(上圖中vt為虛函式表)

為了弄清楚類的記憶體的詳細分配方式,我試圖查出虛函式表中的具體內容和類中的成員函式和成員變數的存放位置。

首先說說我對記憶體的分配的理解:成員函式時存放在**區中,一般不會改變,並且該類的多個例項物件共用該**區,只是每個函式另外會維護乙個我們常見的this指標(this指標主要為了標識特定物件的成員變數,防止多個物件之間的成員函式之間串擾,這就解釋為什麼多個物件共用乙個**區而不會出現問題了,因為每個物件的this指標不一樣啊!),然後成員變數主要和物件儲存在堆或棧中,這些都是每個物件維護自己乙份;

成員變數和物件在乙個連續的空間中,編譯器在呼叫的時候就只要通過簡單的記憶體偏移就可以得到該變數了,那麼,物件如何獲取該物件所對應的類的成員函式呢?這也很簡單,編譯器主要通過物件找到其所屬的類(編譯器一般會維護這樣的關係???),然後找到該類的**區,然後就可以找到相應的成員函式了(虛函式類似,只是虛函式相對於普通函式的靜態編譯改為動態編譯)。

這只是我個人的理解,如有錯誤,還請指正!,編寫如下測試**,兩個類還是上面兩個:

int main()

{ a a; //呼叫預設的建構函式

怎麼????a類的虛函式的位址&(a::vfun)竟然等於1????因為類的成員函式(不管是虛函式還是普通的非靜態函式的函式指標),都沒有過載輸出運算子,編譯器預設輸出就是1(注意!!!如果是取的靜態函式的位址就能得到正確的結果),當我用c風格的printf()函式的時候就能得到正確的結果了:3675337(我輸出的時候轉換成了十進位制了),其他看起來比較正常;

然後按我之前的理解,虛函式表中儲存的虛函式的位址,應該和成員函式指標的到的值應該是一樣的,因為都是共用一套函式**嘛,所以肯定是一樣的(這是錯誤的,下面會分析),另外,如果乙個類有多個物件,那麼這些物件的虛函式表是怎麼分配的呢?是所有的物件共用乙個虛函式表,還是每個物件維護乙個虛函式表呢?其實上面的輸出結果已經很明顯了;

首先,我們通過printf("printf虛函式a.vfun的位址=%d\n",&a::vfun);得到位址3675337;而通過*(int *)*(int *)(&a)得到第乙個虛函式表的位址(是a的虛函式位址)為3674817,這裡又有問題了,為什麼兩者的位址有不同呢???難道有多個**區,而使虛函式的入口函式不一樣?通過void (*fun)()=(void (*)())(*(int *)*(int *)(&a));把虛函式表的第乙個資料轉換為相應的函式指標,然後呼叫該函式指標所指的函式,得到的輸出結果就是對應的虛函式,所以**肯定是一樣的;

後來查詢資料的到如下結論:

另外關於多個物件是否共用乙個虛函式表的問題,通過上面的輸出可知:

c物件和b物件的的虛函式表的位址(對應第乙個函式的位址)都是相等的,所以多個物件共用乙個虛函式表,但兩個物件自身是在不同的記憶體區域的!

下面是a的子類b的物件的記憶體分配:

為什麼b類的&b::vfun和&a::vfun的位址是一樣的呢???求解答!!!

基類和派生類

include include using namespace std class animal class dog public animal class dog public animal這裡的 就是繼承或派生,class cat public animal class item base st...

基類和派生類 this

基類指標在程式執行的時候的確指向的是乙個派生類的物件,但指標的型別仍然是基類指標。c 是一種強型別語言,因此不能用基類指標型別的指標直接呼叫派生類 而且,同乙個類可能有多種不同的派生類,因此不知道實際指向的會是哪個派生類。如果確信是某個派生類的話,可以用這樣的方法來呼叫 cb this b 1 cb...

C 基類和派生類

本講討論基類和派生類的基本概念。通過繼承機制,可以利用已有的資料型別來定義新的資料型別。所定義的新的資料型別不僅擁有新定義的成員,而且還同時擁有舊的成員。我們稱已存在的用來派生新類的類為基類,又稱為父類。由已存在的類派生出的新類稱為派生類,又稱為子類。在c 語言中,乙個派生類可以從乙個基類派生,也可...