菱形繼承定義為:兩個(gè)子類繼承同一個(gè)父類,而又有子類同時(shí)繼承這兩個(gè)父類。 
為了方便理解,畫出如下菱形繼承的對象模型 
代碼描述如下:
#include<iostream>using namespace std; class AA{public: int _a;};class BB : public AA{public: int _b;};class CC : public AA{public: int _c;};class DD :public BB, public CC{public: int _d;};void Test(){ DD d; d.BB::_a = 1; d.CC::_a = 2;}int main(){ Test(); return 0;}
可以看到,在對_a賦值時(shí),必須使用域訪問限定符,否則無法識(shí)別是對BB對象還是對CC對象中的 _a賦值,雖然這樣解決了二義性問題,但是又產(chǎn)生了數(shù)據(jù)冗余的問題,所以,菱形繼承并不是一個(gè)很好的特性。此時(shí)就引入了虛繼承,解決了菱形繼承的二義性和數(shù)據(jù)冗余的問題。 虛繼承是在class BB : public AA與class CC :public AA的public前加上關(guān)鍵字virtual。那么,虛繼承是如何解決二義性的?在解決這個(gè)問題之前,我們先想想另一個(gè)問題:對于菱形繼承與虛繼承,sizeof(b)的值是多大?菱形繼承很容易看出答案是20個(gè)字節(jié),很多人會(huì)想,那虛繼承就是16字節(jié)了,但經(jīng)過測試,發(fā)現(xiàn)應(yīng)該是24字節(jié),怎么會(huì)多出8個(gè)字節(jié)呢,這就涉及到內(nèi)存的分配。現(xiàn)在我們分別在菱形繼承和虛繼承中對_a,_b,_c,_d四個(gè)變量賦值,來觀察這四個(gè)變量的內(nèi)存分配。 將Test()改為如下: void Test() { DD d; d.BB::_a = 0; d.CC::_a = 1; d._b = 2; d._c = 3; d._d = 4; } 首先來看在菱形繼承中的情況如圖:
很明顯的可以看到,對象d占了20個(gè)字節(jié)
可以看到內(nèi)存分配正好符合菱形繼承對象模型中變量的順序 
現(xiàn)在來看在菱形虛擬繼承中的情況如圖:
現(xiàn)在可以明顯看出對象b占了24個(gè)字節(jié)的內(nèi)存,但是,地址0x0049F708和0x0049F710存儲(chǔ)的是什么鬼?在去查看內(nèi)存 
可以看到,0x0049F708和0x0049F710分別存儲(chǔ)了一個(gè)指針,該指針指向的內(nèi)存偏移四個(gè)字節(jié)處存儲(chǔ)的分別是十進(jìn)制值20和12,再來觀察地址0x0049F708和0x0049F710與0x0049F71C相差的字節(jié)數(shù),剛好是20字節(jié)和12字節(jié),所以該值代表了偏移量。 與該內(nèi)存分配對應(yīng)的菱形虛繼承對象模型大致描述如下:
從該內(nèi)存分配圖可以看出,對象_a只有一塊空間,當(dāng)運(yùn)行到d.BB::_a = 0;時(shí),0x0049F71C的內(nèi)容為十進(jìn)制0,當(dāng)運(yùn)行到d.BB::_a = 1;時(shí),其內(nèi)容變?yōu)槭M(jìn)制1。由此可見,變量 _a不再有二義性。這種做法利用了空間上的增加來換取性能的提升,因?yàn)樵诹庑翁摾^承的環(huán)境下,對象d多占了四個(gè)字節(jié)的空間。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注