詳細介紹c++中的類對象內存模型
內存模型描述的是程序中各變量(實例域、靜態域和數組元素)之間的關系,以及在實際計算機系統中將變量存儲到內存和從內存取出變量這樣的低層細節.不同平臺間的處理器架構將直接影響內存模型的結構.
首先介紹一下C++中有繼承關系的類對象內存的布局:
在C++中,如果類中有虛函數,那么它就會有一個虛函數表的指針__vfptr,在類對象最開始的內存數據中。之后是類中的成員變量的內存數據。
對于子類,最開始的內存數據記錄著父類對象的拷貝(包括父類虛函數表指針和成員變量)。之后是子類自己的成員變量數據。
對于子類的子類,也是同樣的原理。但是無論繼承了多少個子類,對象中始終只有一個虛函數表指針。
為了探討C++類對象的內存布局,先來寫幾個類和函數
首先寫一個基類:
- class Base
- {
- public:
- virtual void f() { cout << "Base::f" << endl; }
- virtual void g() { cout << "Base::g" << endl; }
- virtual void h() { cout << "Base::h" << endl; }
- int base;
- protected:
- private:
- };
然后,我們多種不同的繼承情況來研究子類的內存對象結構。
1. 無虛函數集繼承
- //子類1,無虛函數重載
- class Child1 : public Base
- {
- public:
- virtual void f1() { cout << "Child1::f1" << endl; }
- virtual void g1() { cout << "Child1::g1" << endl; }
- virtual void h1() { cout << "Child1::h1" << endl; }
- int child1;
- protected:
- private:
- };
這個子類Child1沒有繼承任何一個基類的虛函數,因此它的虛函數表如下圖:
我們可以看出,子類的虛函數表中,先存放基類的虛函數,在存放子類自己的虛函數。
2. 有一個虛函數繼承
- //子類2,有1個虛函數重載
- class Child2 : public Base
- {
- public:
- virtual void f() { cout << "Child2::f" << endl; }
- virtual void g2() { cout << "Child2::g2" << endl; }
- virtual void h2() { cout << "Child2::h2" << endl; }
- int child2;
- protected:
- private:
- };
當子類重載了父類的虛函數,則編譯器會將子類虛函數表中對應的父類的虛函數替換成子類的函數。
3. 全部虛函數都繼承
- //子類3,全部虛函數重載
- class Child3 : public Base
- {
- public:
- virtual void f() { cout << "Child3::f" << endl; }
- virtual void g() { cout << "Child3::g" << endl; }
- virtual void h() { cout << "Child3::h" << endl; }
- protected:
- int x;
- private:
- };
#p#
4. 多重繼承
多重繼承,即類有多個父類,這種情況下的子類的內存結構和單一繼承有所不同。
我們可以看到,當子類繼承了多個父類,那么子類的內存結構是這樣的:
子類的內存中,順序
5. 菱形繼承
6. 單一虛擬繼承
虛擬繼承的子類的內存結構,和普通繼承完全不同。虛擬繼承的子類,有單獨的虛函數表, 另外也單獨保存一份父類的虛函數表,兩部分之間用一個四個字節的0x00000000來作為分界。子類的內存中,首先是自己的虛函數表,然后是子類的數據成員,然后是0x0,之后就是父類的虛函數表,之后是父類的數據成員。
如果子類沒有自己的虛函數,那么子類就不會有虛函數表,但是子類數據和父類數據之間,還是需要0x0來間隔。
因此,在虛擬繼承中,子類和父類的數據,是完全間隔的,先存放子類自己的虛函數表和數據,中間以0x分界,最后保存父類的虛函數和數據。如果子類重載了父類的虛函數,那么則將子類內存中父類虛函數表的相應函數替換。
7. 菱形虛擬繼承
結論:
(1) 對于基類,如果有虛函數,那么先存放虛函數表指針,然后存放自己的數據成員;如果沒有虛函數,那么直接存放數據成員。
(2) 對于單一繼承的類對象,先存放父類的數據拷貝(包括虛函數表指針),然后是本類的數據。
(3) 虛函數表中,先存放父類的虛函數,再存放子類的虛函數
(4) 如果重載了父類的某些虛函數,那么新的虛函數將虛函數表中父類的這些虛函數覆蓋。
(5) 對于多重繼承,先存放第一個父類的數據拷貝,在存放第二個父類的數據拷貝,一次類推,最后存放自己的數據成員。其中每一個父類拷貝都包含一個虛函數表指針。如果子類重載了某個父類的某個虛函數,那么該將該父類虛函數表的函數覆蓋。另外,子類自己的虛函數,存儲于第一個父類的虛函數表后邊部分。
(6) 當對象的虛函數被調用是,編譯器去查詢對象的虛函數表,找到該函數,然后調用。
到這c++類對象的內存模型就介紹完了,希望對大家有幫助。
【編輯推薦】