前言:僅以本文獻給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 就是虛表指針。