程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++內存中的封裝、繼承、多態(上)

C++內存中的封裝、繼承、多態(上)

編輯:C++入門知識

 

一、封裝模型的內存布局

常見類對象的成員可能包含以下元素:內建類型、指針、引用、組合對象、虛函數。

另一個角度的分類:

數據成員:

成員函數:

 



類中的內建類型按照聲明的順序在內存中連續存儲,並且分配的大小由內建類型本身的大小決定(依賴機器),布局受字節對齊影響(本篇不討論字節對齊)

 

&*     (*

存儲方式同1的場合,不同點為指針和引用通常為固定大小(32位機器4字節、64位機器8字節)。

有關引用:個人理解的引用就是懶人專用指針,取地址又間地址是很麻煩的操作,於是出現了自動取址又間址的指向常量的常指針

在類中聲明可以測出固定字節大小,所以也是占用固定的字節大小。

 



內存布局圖示(本篇以及後續篇使用的環境為 32位Win7, VS2008):

再來看一下地址:

結論:(顯而易見就不解釋了)

類對象最終被解釋成內建類型,布局依然按照聲明的順序,並且對象布局在內存中依然是連續的

 

  

內存布局圖示

 

通過程序輸出看一下

typedef  (*<<<<(*)&t<<<<<<(*)*(*)&t<<<<= (PF)*(*)*(*)&<<= (PF)*(*)*(*)& 

輸出圖示

推理證明:

1.取t的地址強轉成(int*)類型輸出以後得到的地址 == 取t的vfptr的地址(調試窗口第一行): 虛函數指針被放在對象布局的首地址位置

2.因為(int*)&t == vfptr,那麼*vfptr得到的是虛函數表的首地址。

(int*)*vfptr,把虛函數表的首地址強轉成(int*)的地址 == t對象的__vftable的虛函數表的地址(調試窗口第四行行):虛函數指針指向虛函數表

3.vftable的首地址到vftable的第一個函數的地址中間相差很多空間:虛函數表還承擔了虛函數以外的內容

什麼內容也會放在虛函數表中呢?

虛函數表用來實現多態,多態意味著類型上的模糊,模糊以後必須有東西來記錄自己的老本,否則無法實現另外一個東西——RTTI

結論:

在包含虛函數的場合多了一個vfptr,它是一個const指針,位於類布局中的首位置,指向了虛函數表,虛函數表包含了虛函數地址,通過虛函數地址訪問虛函數。

並且虛函數表的首地址存在了本類的類型信息,用於實現RTTI。

 

 

static的特性眾所周知,從調試窗口觀察變量並不能得出什麼結論,我們先列出幾條特性:

1.static成員為整個類共有的屬性

2.static函數不包含this指針

3.static成員不能訪問nonstatic成員

初步結論:

內存對象模型中對static作了隔離處理(不是所有對象具有的),static自己獨霸一方。

 

通過以上5條現在來構建C++的封裝模型:

 

有關普通的成員函數

所謂類,就是自己圈定了一個域名,所以在內存中的代碼區也圈定了自己的域,普通的成員函數放在那裡。

有關靜態成員函數

在代碼區中圈定的類域名中的圈定一個static區域,思路依然是獨霸一方。

有關構造函數

由於構造函數的特殊性,所以在代碼區擁有一個自己的構造代碼區域。

現在又有了一個更完整的模型:

假定讀者已經了解堆/棧/靜態區和常量區/代碼區

 

 

根據上圖我們得到一些結論

1.類最終被解釋內建類型(內建類型過了編譯期以後,都不復存在,只是編譯期的解讀方式而已)

2.內建類型按照聲明的次序順序存儲

3.存在虛函數的場合,會生成vfptr,並且vfptr->vtable->function()

4.靜態成員被單獨對待、數據只有一份拷貝,函數被放到static區域。

5.Type Infomation被放到vftable中

 

二、封裝模型的構造過程

1.靜態是編譯期決定的,所有對象共有的數據拷貝,優先創建。

2.進入構造函數,優先創建vfptr和vftable,也就是優先構造虛函數部分

3.其次按照聲明的順序構造數據成員。

我們可以使用逗號表達式來干一些有意思的事情。

事先我們需要定義

(*PF)();
PF pf = NULL;

<<, )), a((cout<<,   fun(){cout<<<<=(PF)*(*)*(*), pf()), cout<<, )), data2((cout<<, 
    fun(){cout<<<<  T::sdata1 = (cout<<, );

 

以下是程序運行的結果:

 靜態--虛函數表--聲明次序初始化。

 

文章不免有疏忽和不足的地方,歡迎大家批評指正。郵箱【[email protected]

下一篇重點講繼承時和多態時的內存布局。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved