C++繼承時的物件記憶體模型
推薦閱讀:http://blog.csdn.net/randyjiawenjie/article/details/6693337
最近研究了一下,C++繼承的記憶體物件模型。主要是讀了讀http://blog.csdn.net/haoel/article/details/3081328(C++ 物件的記憶體佈局)。很推薦這篇文章。
對這篇文章做了做總結。本文的大部分內容來自於這篇文章中的總結http://blog.csdn.net/haoel/article/details/3081328(C++ 物件的記憶體佈局
#類中的元素
0. 成員變數 1. 成員函式 2. 靜態成員變數 3. 靜態成員函式 4. 虛擬函式 5. 純虛擬函式
#影響物件大小的因素
0. 成員變數 1. 虛擬函式表指標(_vftptr) 2. 虛基類表指標(_vbtptr) 3.
_vftptr、_vbtptr的初始化由物件的建構函式, 賦值運算子自動完成;物件生命週期結束後,由物件的解構函式來銷燬。
物件所關聯的型別(type_info),通常放在virtual table的第一個slot中。
虛繼承:在繼承定義中包含了virtual關鍵字的繼承關係;
虛基類:在虛繼承體系中的通過virtual繼承而來的基類,需要注意的是:
class CDerive : public virtual CBase {}; 其中CBase稱之為CDerive的虛基類,而不是說CBase就是個虛基類,因為CBase還可以為不是虛繼承體系中的基類。
虛擬函式被派生後,仍然為虛擬函式,即使在派生類中省去virtual關鍵字。
虛基類的構造與析構是由最終子類負責呼叫的(而不是直接派生子類)
注:【下文中_vbptr即_vbtptr】
#物件記憶體佈局分類討論
vc6變數檢視器中(Locals,Watch1等),也可以看到部分物件佈局的情況(不完整,且虛繼承是錯誤的)。
vs2005及以後版本的編譯器提供了/d1reportSingleClassLayout[類名]編譯選項來檢視物件完整的記憶體佈局:
cl classLayout.cpp /d1reportSingleClassLayoutCChildren
注:下文舉例的類圖中函式均為虛擬函式(斜體 表示該函式為虛擬函式)
0. 單一類
(1). 空類
sizeof(CNull)=1(用於標識該物件)
(2). 只有成員變數的類
int nVarSize = sizeof(CVariable) = 12
記憶體佈局:
(3). 只有虛擬函式的類
int nVFuntionSize = sizeof(CVFuction) = 4(虛表指標)
記憶體佈局:
(4). 有成員變數、虛擬函式的類
int nParentSize = sizeof(CParent) = 8
記憶體佈局:
1. 單一繼承(含成員變數、虛擬函式、虛擬函式覆蓋)
int nChildSize = sizeof(CChildren) = 12
vc中顯示的結果(注:還有1個虛擬函式CChildren::g1沒有被顯示出來):
d1reportSingleClass檢視:
記憶體佈局:
2. 多繼承 (含成員變數、虛擬函式、虛擬函式覆蓋)
int nChildSize = sizeof(CChildren) = 20
vc中顯示的結果(注:還有2個虛擬函式CChildren::f2,CChildren::h2沒有被顯示出來,this指標的adjustor[調整值]也沒打印出):
d1reportSingleClass檢視:
記憶體佈局:
3. 深度為2的繼承(含成員變數、虛擬函式、虛擬函式覆蓋)
int nGrandSize = sizeof(CGrandChildren) = 24
vc中顯示的結果(注:還有3個虛擬函式CGrandChildren::f2,CChildren::h2,CGrandChildren::f3沒有顯示出來,this指標的adjustor[調整值]也沒打印出):
d1reportSingleClass檢視:
記憶體佈局:
4 重複繼承(含成員變數、虛擬函式、虛擬函式覆蓋)
int nGrandSize = sizeof(CGrandChildren) = 28
vc中顯示的結果(注:還有大量的虛擬函式沒有顯示出來,this指標的adjustor[調整值]也沒打印出):
thunk函式:一種形實轉換輔助函式;主要做this指標調整,函式呼叫重定向。
d1reportSingleClass檢視:
記憶體佈局:
由於m_nAge在內容中存在兩個拷貝,因此我們不能直接通過pGrandChildrenA->m_nAge來訪問該變數,
這樣會存在二義性,編譯器無法知道應該訪問CChildren1中的m_nAge,還是CChildren2中的m_nAge。
為了標識唯一的m_nAge,就需要帶上其所在範圍的類名了。如下:
1 pGrandChildrenA->CChildren1::m_nAge = 1; 2 pGrandChildrenA->CChildren2::m_nAge = 2;
5. 單一虛繼承(含成員變數、虛擬函式、虛擬函式覆蓋)
int nChildSize = sizeof(CChildren) = 20
d1reportSingleClass檢視:
記憶體佈局:
6. 多虛繼承(含成員變數、虛擬函式、虛擬函式覆蓋)
(1) virtual CParent1, CParent2
int nChildSize = sizeof(CChildren) = 24
d1reportSingleClass檢視:
記憶體佈局:
(2) CParent1, virtual CParent2
int nChildSize = sizeof(CChildren) = 24
d1reportSingleClass檢視:
記憶體佈局:
(3) virtual CParent1, virtual CParent2
int nChildSize = sizeof(CChildren) = 28
d1reportSingleClass檢視:
記憶體佈局:
7. 鑽石型的虛擬多重繼承(含成員變數、虛擬函式、虛擬函式覆蓋)
int nGrandChildSize = sizeof(CGrandChildren) = 36
d1reportSingleClass檢視:
thunk函式:一種形實轉換輔助函式;主要做this指標調整,函式呼叫重定向。
記憶體佈局: