C++ 記憶體布局(二) 虛繼承 —Empty virtual base classs (空虛基類)

來源:互聯網
上載者:User

上一次只是給出一個代碼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:太困,眼睛都睜不開了~~~~不行了,要睡覺了~~~~ 難免有打錯的地方,請原諒

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.