類的構造(2):對象的記憶體布局

來源:互聯網
上載者:User

一個類對象在記憶體中總占幾個位元組,哪些內容分別佔多少位元組呢? 我們舉個例子來瞧下.

//由於存在啥記憶體對齊,所以你用sizeof去檢查類的size時會不準確.所以這裡使用個#pragma pack(1)顯式指定記憶體對齊模式會使結果比較準確點.

//記憶體對齊詳細解釋見http://baike.baidu.com/view/4786260.htm

簡單樣本1:靜態變數,函數不佔用類對象記憶體

#pragma pack(1)

class human{

int ID;  //佔4個位元組

char ch; //佔1位元組

static int age; //佔4位元組

};

 

class father : public human{

void Test();

};

 

void main()

{

   cout<< sizeof human;   //大小為5,而不是你期望的9,因為static型的成員變數不屬於類對象,它是放在靜態儲存區裡面去了.

  cout<<sizeof father ; //大小也是5,把human中的內容原封不動的拷貝一份放到father裡面來的.要是類比下嘛,跟標頭檔類似,引用標頭檔時實際上相當於把所有內容複寫過來.

                                //當然只是類似,有時是原封不動拷,但有時只是選擇性的拷貝的.

                                //我們看到函數是不佔用類對象的記憶體的,函數是放到代碼區.

}

 

樣本2:虛函數的的特殊處理

假如類中有虛函數那麼類對象中會額外隱式添加一個指標,指向一個虛擬函數表.

class human{

 int ID; //4位元組

 char ch; //1位元組

 virtual void Test();

}

 

cout<<sizeof human; //大小為9,這裡假設是32位系統,一個指標佔4位元組.

 

你可能會問那麼虛擬函數表是啥東東啊?怎麼實現的啊.

具體怎麼實現的我也不清楚,沒看到過相關資料.不過我們可以從MFC構架的一些思想裡猜想下.MFC中最愛乾的事就是在類中整一堆宏嘛,來實現啥動態類型識別,動態建立之類的.實際上就是在類中聲明一個static的結構體CRuntimeClass,把相關資訊都放這結構體裡.

我們這裡來猜想下,類human中可能會這樣寫

class human{

int ID;

char ch;

virtual void Test();

static list<FunNode> funList; //這裡就簡化處理了,一個鏈表,每個節點是個函數指標.比如可以添加節點funList.add(Test);

list<FunNode>* vptr; //指向上面那個funList的指標.所以有虛函數時多出的4個位元組記憶體就是它了.不管有多少個虛函數也只多它一個指標

}

 

那假如有類father繼承類human會怎麼樣呢?

father會建立自己的虛函數表,只不過會把human中虛函數表內容拷過來.然後建立一個指標vptr指向自己建立的虛函數表.

 

樣本3:虛擬繼承

我們知道多重繼承中,有個典型的缺陷就是會有歧義產生,而解決辦法就是用虛擬繼承.樣本

非虛擬繼承

class human{

public:

int ID; //4位元組

char ch; //1位元組

};

class father: public  human{

};

class mather: public human{

};

 

class son: public father, public mather{

};

 

cout<<sizeof human;  //5位元組

cout<<sizeof father <<sizeof mather; //都是5位元組

cout<<sizeof son; //10位元組father與mather的和嘛

上面的記憶體布局很好理解,father和mather拷貝一份human的內容.而son又把father與mather的拷貝過來.

我們知道如果father與mather的繼承中如果不加virutal這關鍵字的話,則這樣使用會報錯: Son so;

int num = so.ID; //因為son中有從father拷過的ID,也有從mather拷來的ID,編譯器不知道去找哪個了.不過你可以這樣顯式的調用

int num = so.father::ID;//或者so.mather::ID;

 

虛擬繼承

當然上面這樣做很麻煩,所以有這樣一個解決方案

class father: public virtual human{

};

class mather: public virtual human{

};

上面其他不變,只是加上virtaul關鍵字.

cout<<sizeof human; //仍為5

cout<<sizeof  father<<sizeof mather; //都為9

cout<<sizeof son<<為13

這個結果有點不可思議吧.

 

當使用虛擬繼承之後,father除了拷貝human中的5個位元組外還增加一個指標(4位元組),指向human的指標.所以大小為9

而son是先從human中拷貝5位元組過來,然後拷貝father中非human的部分4位元組,拷貝mather中非human的部分4位元組.所以加起來是13.

 

假如father中還另外定義個成員,比如int money; //則son也還拷過來.這樣會在13基礎上再加4.

反正記住的一點就是human的在father,mather中會拷去兩份相同的內容.而son只會拷一份過來.當然為了保證這個操作額外的代價是多出些指標來了啊.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.