前言:僅以本文獻給C++初學者,高手請繞道。測試環境VS2013
PS:在VS2013中,debug時,CC CC CC CC 4個位元組是棧中對象的分割線
一、單重繼承不帶虛函數的類對象記憶體結構
測試代碼如下:
class Parent{
public:
Parent() :pdata(0x11111111){} //将父類的pdata初始化為0x11111111
private:
int pdata;
};
class Son : public Parent{
public:
Son():sdata(0x22222222){} //将子類的sdata初始化為0x22222222
private:
int sdata;
};
int main(int argc, char** argv){
Son son;
return 0; //在此行設定斷點,觀察son的記憶體結構
}

通過調試觀察son的記憶體,可以看到在簡單的單重繼承不帶虛函數的應用場景中,子類對象所占用的位元組數,等于各成員占有位元組數之和。
在我們這個測試場景中占8個位元組 = sizeof(sdata) + sizeof(pdata)
二、單重繼承帶虛函數的類對象記憶體結構
class Parent{
public:
Parent() :pdata(0x11111111){}
virtual void ParentVirtualFunction(void){} //虛函數
private:
int pdata;
};
class Son : public Parent{
public:
Son():sdata(0x22222222){}
private:
int sdata;
};
int main(int argc, char** argv){
Parent parent;
Son son;
return 0; //在此行設定斷點,觀察兩個對象的記憶體狀況
}
a、我們先看看parent的記憶體狀況
parent的位址是:0x0016f984,這個位址所執向的記憶體包含兩個東西:一個虛表指針,一個pdata資料。因為Parent類含有虛函數,是以parent對象的前四個位元組存放的
是虛表的位址,我們可以看到虛表位址是:0x00b8cd20。關于虛表在這裡不做讨論。
接着我們看下parent對象的記憶體布局吧:
通過上面的截圖,我們看到parent位址所指向的8個位元組的前4個位元組就是虛表指針,後4個位元組是pdata。整個對象占用8個位元組
b、現在我們來看看son的記憶體狀況
觀察上面的截圖,可以看出son由兩部分組成:一部分是從父類繼承過來的資料,還有一部分是自己本身的資料
接着我們看下實體記憶體:
實體記憶體大小占12個位元組,前4個位元組是虛表指針,後8個位元組是資料成員
3.單重虛繼承不帶虛函數的類對象記憶體結構
class Parent{
public:
Parent() :pdata(0x11111111){}
void ParentVirtualFunction(void){}
private:
int pdata;
};
class Son : virtual public Parent{
public:
Son():sdata(0x22222222){}
private:
int sdata;
};
int main(int argc, char** argv){
Parent parent;
Son son;
Parent* p1 = &son;
Son* p2 = &son;
return 0;
}
首先看下圖,請注意虛繼承時,p1 和 p2的值是不一樣的。p1的類型是Parent*,在虛繼承時,将子類對象的位址
指派給父類指針,編譯器會将子類對象中屬于父類的那部分成員的起始位址指派給父類指針。是以我們看到p1的值不等于
p2的值。
觀察下圖,我們可以看到p1指向11 11 11 11 (pdata)
再次觀察上面的截圖,可以發現除了sdata,pdata,還有4個位元組00 41 cc 70,這4個位元組存放的是一個特殊結構體的指針,這個特殊結構體的類型如下:
struct TagOffset {
char unKnow[4];
int nOffset;
};
這結構體中nOffset字段值表示父類成員在子類對象中的偏移量。在本例中這個結構體指針的值是:00 41 cc 70,下面我們看下這個指針指向的記憶體:
上周中後四個位元組就是nOffset, 值等于8, 表示分類成員在子對象中的偏移量是8:如下圖:
11 11 11 11 在子類對象中的偏移量是8
4、單重虛繼承帶虛函數的類對象記憶體結構
class Parent{
public:
Parent() :pdata(0x11111111){}
virtual void ParentVirtualFunction(void){}
private:
int pdata;
};
class Son : virtual public Parent{ //子類沒有重寫父類的虛函數,如果重寫記憶體布局會稍有不同
public:
Son():sdata(0x22222222){}
private:
int sdata;
};
int main(int argc, char** argv){
Parent parent;
Son son;
Parent* p1 = &son;
Son* p2 = &son;
return 0;
}
和第三種情況相比,因為父類帶有虛函數,是以子類存在虛表,74 CC 41 00 就是虛表指針。