關(guān)于C++中菱形繼承和虛繼承的問(wèn)題總結(jié)
前言
菱形繼承是多重繼承中跑不掉的,Java拿掉了多重繼承,輔之以接口。C++中雖然沒有明確說(shuō)明接口這種東西,但是只有純虛函數(shù)的類可以看作Java中的接口。在多重繼承中建議使用“接口”,來(lái)避免多重繼承中可能出現(xiàn)的各種問(wèn)題。本文將給大家詳細(xì)介紹關(guān)于C++菱形繼承和虛繼承的相關(guān)內(nèi)容,分享出來(lái)供大家參考學(xué)習(xí),話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧。
繼承:
1. 單繼承–一個(gè)子類只有一個(gè)直接父類時(shí)稱這個(gè)繼承關(guān)系為單繼承
2. 多繼承–一個(gè)子類有兩個(gè)或以上直接父類時(shí)稱這個(gè)繼承關(guān)系為多繼承
例如下面這兩個(gè)例子:
例一(單繼承):
class A { public: int _a; }; class B : public A // B是 子類/派生類, 公有 繼承父類/基類 A { public: int _b; }; class C : public B //C是 子類/派生類, 公有繼承 父類/基類 B { public: int _c; };
例二(多繼承):
class A { public: int _a; }; class B { public: int _b; }; class C : public A , public B // 子類C同時(shí)公有繼承父類A和父類B { public: int _c; };
用圖很形象的表示一下:
但是在使用過(guò)程中,很容易出現(xiàn)一種繼承關(guān)系叫菱形繼承。就好比下面這種繼承方式。
class A { public: int _a; }; class B : public A { public: int _b; }; class C : public A { public: int _c; }; class D : public B, public C { public: int _d; };
繼承的方式簡(jiǎn)單畫出來(lái)就是下面這樣:
我們?cè)谑褂眠^(guò)程中會(huì)發(fā)現(xiàn)以下缺點(diǎn):
1、 當(dāng)我們用D類創(chuàng)建出對(duì)象d時(shí),可以訪問(wèn)到_a,但是一旦編譯就會(huì)出現(xiàn)錯(cuò)誤。錯(cuò)誤說(shuō)明為: C2385: 對(duì)“_a”的訪問(wèn)不明確。從圖中也可以看出,如果用d訪問(wèn)_a時(shí),可能在B類里,也同時(shí)可能存在于c類中。這就是所謂的“二義性”;
2、雖然B類和C類都公有繼承A,但是在D類公有繼承B,C時(shí),存放了兩份A類,造成了數(shù)據(jù)的冗余。
C++針對(duì)這種缺陷提出了另外一種繼承方式叫做虛繼承。
虛繼承
C++使用虛擬繼承(Virtual Inheritance),解決從不同途徑繼承來(lái)的同名的數(shù)據(jù)成員在內(nèi)存中有不同的拷貝造成數(shù)據(jù)不一致問(wèn)題,將共同基類設(shè)置為虛基類。這時(shí)從不同的路徑繼承過(guò)來(lái)的同名數(shù)據(jù)成員在內(nèi)存中就只有一個(gè)拷貝,同一個(gè)函數(shù)名也只有一個(gè)映射。
◇語(yǔ)法:
class 派生類: virtual 基類1,virtual 基類2,…,virtual 基類n { …//派生類成員聲明 };
在有了虛繼承的概念后,我們就可以規(guī)避上面的缺點(diǎn)了。
class A { public: int _a; }; class B : virtual public A { public: int _b; }; class C : virtual public A { public: int _c; }; class D : public B, public C { public: int _d; };
當(dāng)我們使用了虛繼承時(shí),繼承模型就改變?yōu)橄旅孢@樣:
由于我所使用的是vs2015,在此編譯器下對(duì)應(yīng)的處理方式就是這樣。將class B 和 class C設(shè)置為虛繼承后,編譯器將class A存放在了最下端,并在B和C類的前四個(gè)字節(jié)中存放了一個(gè)地址,當(dāng)我們?cè)L問(wèn)過(guò)去向下再多看四個(gè)字節(jié)時(shí)就會(huì)發(fā)現(xiàn)這其中存放了一個(gè)數(shù)字。而這個(gè)數(shù)字就類似于“偏移量”,記錄了該類的首地址距父類首地址之間的字節(jié)差距。比如class B中,我們找到對(duì)應(yīng)數(shù)字為14,但是這個(gè)數(shù)字是16進(jìn)制,轉(zhuǎn)為10進(jìn)制為20,在class B的首地址加上20個(gè)字節(jié)就恰好是class A的首地址,同理class C。
因此在class D訪問(wèn)_a時(shí),就不會(huì)產(chǎn)生二義性,_a數(shù)據(jù)也只存放了一份,解決了之前菱形繼承所帶來(lái)的問(wèn)題。
但是還存在一個(gè)問(wèn)題:當(dāng)我們求沒有使用虛繼承之前的class D的大小,結(jié)果是20,但是在使用了虛繼承后大小變?yōu)?4。所以雖然使用虛繼承解決數(shù)據(jù)冗余問(wèn)題也帶來(lái)了性能上的損耗。(關(guān)于如何計(jì)算內(nèi)存大小,可以參考此鏈接。)
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
C++中函數(shù)使用的基本知識(shí)學(xué)習(xí)教程
這篇文章主要介紹了C++中函數(shù)使用的基本知識(shí)學(xué)習(xí)教程,涵蓋了函數(shù)的聲明和參數(shù)以及指針等各個(gè)方面的知識(shí),非常全面,需要的朋友可以參考下2016-01-01C++在C語(yǔ)言基礎(chǔ)之上增強(qiáng)的幾個(gè)實(shí)用特性總結(jié)
這篇文章主要介紹了C++在C語(yǔ)言基礎(chǔ)之上增強(qiáng)的幾個(gè)實(shí)用特性總結(jié),包括C++中更強(qiáng)的類型約束以及結(jié)構(gòu)體方面等一些更加高級(jí)的特性,需要的朋友可以參考下2016-03-03C語(yǔ)言單循環(huán)鏈表的表示與實(shí)現(xiàn)實(shí)例詳解
這篇文章主要介紹了C語(yǔ)言單循環(huán)鏈表的表示與實(shí)現(xiàn),對(duì)于學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法的朋友來(lái)說(shuō)很有參考借鑒價(jià)值,需要的朋友可以參考下2014-07-07Visual Studio Code 配置C、C++環(huán)境/編譯并運(yùn)行的流程分析
這篇文章主要介紹了Visual Studio Code 配置C、C++環(huán)境/編譯并運(yùn)行的流程分析,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05C++入門之基礎(chǔ)語(yǔ)法學(xué)習(xí)教程
這篇文章主要介紹了C++入門之基本語(yǔ)法學(xué)習(xí)教程,列出了C++的關(guān)鍵字,同時(shí)講解了注釋的寫法,需要的朋友可以參考下2016-05-05C語(yǔ)言詳解實(shí)現(xiàn)猜數(shù)字游戲步驟
猜數(shù)字是興起于英國(guó)的益智類小游戲,起源于20世紀(jì)中期,一般由兩個(gè)人或多人玩,也可以由一個(gè)人和電腦玩。游戲規(guī)則為一方出數(shù)字,一方猜,今天我們來(lái)實(shí)現(xiàn)這個(gè)游戲案例2022-07-07C語(yǔ)言使用鏈表實(shí)現(xiàn)學(xué)生籍貫管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言使用鏈表實(shí)現(xiàn)學(xué)生籍貫管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02C++詳解Primer文本查詢程序的實(shí)現(xiàn)
這個(gè)程序還是比較復(fù)雜的,把這句話作為文章的開頭可以看出它的真實(shí)性.....這篇文章主要介紹了文本查詢程序的實(shí)現(xiàn),下面我們一起來(lái)看看2022-06-06C++多字節(jié)字符與寬字節(jié)字符相互轉(zhuǎn)換
最近在C++編程中經(jīng)常遇到需要多字節(jié)字符與寬字節(jié)字符相互轉(zhuǎn)換的問(wèn)題,自己寫了一個(gè)類來(lái)封裝wchar_t與char類型間的轉(zhuǎn)換2012-11-11