上一次只是給出一個代碼http://blog.csdn.net/OpenHero/archive/2006/04/02/648005.aspx,和運行過程中的記憶體位置,現在具體的按照步驟分析一下C++中虛擬繼承中的情況,
由於C++ stand(C++ 標準委員會) 並沒有規定具體實現的辦法,各個編譯器廠商可能都採用不同的實現方法,
或許在不同的年代使用的方法也不相同,有可能就會得到不同的結果,這裡,我採用VC7.1作為調試工具,由潛
入深的一步步分析C++ 虛繼承中的記憶體布局;
這一部分由Empty virtual base calss (空虛基類:空的calss)入手,查看VC7.1中是如何?虛擬繼承的.
下面給出測試的代碼:
一,菱形的繼承模式
class A
{ };
class B : virtual public A
{
};
class C : virtual public A
{
};
class D : public C, public B
{
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
B b;
C c;
D d;
int *pd = (int*)&d;
int *pd_c = (int*)(C*)(&d);
int *pd_b = (int*)(B*)(&d);
int *pd_a = (int*)(A*)(&d);
cout<< sizeof(a) << endl;
cout<< sizeof(b) << endl;
cout<< sizeof(c) << endl;
cout<< sizeof(d) << endl;
cout<< pd << endl;
cout<< pd_c << endl;
cout<< pd_b << endl;
cout<< pd_a << endl;
return 0;
}
有人會問,
class A
{ };
A a, 其中的a會有大小嘛?當然,a裡面什麼都沒有,姑且就認為它是空的吧,但是,編譯器如何才能記住a,而又
是空?你能想到一種實現的方法嘛?所以,編譯器為了記住空的a,在a的內部放入了一個char的標記,作為它的
唯一標識,這樣編譯器才能認識那個a. 如果你有興趣,你可以嘗試定義A a,b; 然後你看看a和b是否是一
樣的?現階段來說,應該是不一樣的,或許以後誰會發明新的實現方式,那個時候或許是一樣也有可能了。
現在讓我們看看上面程式的輸出情況,如下:
1
4
4
8
0012FEAC
0012FEAC
0012FEB0
0012FEB4
這裡我先不對上面的輸出做出解釋,或許你看了這樣的輸出會有疑惑,是不是和你想象的不一樣?帶著疑問
繼續往下面看。
下面我再給出兩段代碼,同時給出他們各自的輸出,然後再對這些輸出做出解釋:
二,六邊形(菱形一)
class A
{ };
class B : virtual public A
{
};
class C : virtual public A
{
};
class D : /*virtual */public B
{
};
class E : /*virtual */public C
{
};
class F : public E, public D
{
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
B b;
C c;
D d;
E e;
F f;
int *pf = (int*)&f;
int *pf_e = (int*)(E*)(&f);
int *pf_d = (int*)(D*)(&f);
int *pf_c = (int*)(C*)(&f);
int *pf_b = (int*)(B*)(&f);
int *pf_a = (int*)(A*)(&f);
cout<< sizeof(a) << endl;
cout<< sizeof(b) << endl;
cout<< sizeof(c) << endl;
cout<< sizeof(d) << endl;
cout<< sizeof(f) << endl;
cout<< pf << endl;
cout<< pf_e << endl;
cout<< pf_d << endl;
cout<< pf_c << endl;
cout<< pf_b << endl;
cout<< pf_a << endl;
return 0;
}
輸出結果:
1
4
4
4
4
8
0012FE94
0012FE94
0012FE98
0012FE94
0012FE98
0012FE9C
三,六邊形(菱形二)
class A
{ };
class B : virtual public A
{
};
class C : virtual public A
{
};
class D : virtual public B
{
};
class E : virtual public C
{
};
class F : public E, public D
{
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
B b;
C c;
D d;
E e;
F f;
int *pf = (int*)&f;
int *pf_e = (int*)(E*)(&f);
int *pf_d = (int*)(D*)(&f);
int *pf_c = (int*)(C*)(&f);
int *pf_b = (int*)(B*)(&f);
int *pf_a = (int*)(A*)(&f);
cout<< sizeof(a) << endl;
cout<< sizeof(b) << endl;
cout<< sizeof(c) << endl;
cout<< sizeof(d) << endl;
cout<< sizeof(e) << endl;
cout<< sizeof(f) << endl;
cout<< pf << endl;
cout<< pf_e << endl;
cout<< pf_d << endl;
cout<< pf_c << endl;
cout<< pf_b << endl;
cout<< pf_a << endl;
return 0;
}
輸出結果:
1
4
4
8
8
16
0012FE84
0012FE84
0012FE88
0012FE8C
0012FE90
0012FE8C
下面對其中的一,二做分析,三是一個有趣的現象,你可以考慮考慮:)
可以看到,只要有virtual的地方,都多出4個位元組,用來幹嘛?
在VC的編譯器中,採用了virtual base calss pointer的方式來實現虛繼承,從而保證子類回溯到基類的時
候只存在一個實體。多出來的4個位元組就是用來存放各自的指向virtual base class 的地址的。
你可能又要問,a的大小為1,b,c……的大小隻是4個4個增加,考慮記憶體對其,那不是會更大嗎?
看到前面提到的一個問題了嗎?
編譯器只是為了記住a的時候才把a標示出來,如果知道a是空的,它既然有了子類,子類表示出來的時候,
當然基類就存在其中了:)
你可能又問? 那為什麼不把子類也表示為1,不就行了嗎?當然不行,就像上面提到的,因為是為了實現虛
繼承的菱形一樣的繼承樣式,在子類中只存在基類一個實體,所以,就才用了指標的方式,而不是把整個實
體放到子類的記憶體中。
在以後的內容中,會不斷添加參數,函數,虛函數,分析虛繼承中記憶體的分布。
聲明:如果有網站轉載,請把出處和串連加入到網頁上,謝謝,咱,熬夜很困阿~~~
ps:太困,眼睛都睜不開了~~~~不行了,要睡覺了~~~~ 難免有打錯的地方,請原諒