一個類對象在記憶體中總占幾個位元組,哪些內容分別佔多少位元組呢? 我們舉個例子來瞧下.
//由於存在啥記憶體對齊,所以你用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只會拷一份過來.當然為了保證這個操作額外的代價是多出些指標來了啊.