菱形繼承
我們都知道,c++中有單繼承和多繼承兩種繼承方式:
單繼承:如果乙個類只有乙個直接基類(也就是父類),我們稱這種繼承方式為單繼承。
多繼承:如果乙個類有多個父類(2個及其兩個以上),我們稱這種繼承方式為多繼承。
什麼是菱形繼承?
就講清楚菱形繼承,單單靠文字描述很難講清,所以直接上圖和**:
先看乙個最簡單的菱形繼承的**::
詳解:::
我們可以通過除錯看一下監視視窗:
什麼是二義性呢???
舉乙個很簡單的例子:::
int main()
編譯器會報錯,因為b和c都繼承了a,而且d繼承了b和c。所以d中會存在兩個a,那麼編譯器就不知道你指定的_a 是哪個類裡面的,所以會報錯說,呼叫不明確。
當然就想給b或者c中的_a賦值,還是有辦法的,那就是指定作用域。例如:
d.b
::_a = 1;
//b中的_a
d.c::_a = 2;
//c中的_a
那麼什麼又是資料冗餘呢?
資料冗餘是指資料之間的重複,也可以說是同一資料儲存在不同資料檔案中的現象。
也就是說d中存在兩個_a ,這種現象就叫做資料冗餘。
如果是常規的菱形繼承,就會導致二義性和 資料冗餘問題。那麼怎麼解決這一問題,c++中有一種方法:「用虛擬繼承解決」
能保證繼承後父類的記憶體布局只會存在乙份
#include
using
namespace
std;
class a
public:
int _a;
};class b :virtual
public a//虛繼承
;class c:virtual
public a//虛繼承
;class d:public b,public c
int _d;
};int main()
實際上,我們可以抽象出它的物件模型
d中只存在乙份a,這樣就可以避免出現二義性和資料冗餘現象。
當然這些不是空口無憑的,我們可以在vs2008的環境下看看記憶體中到底是怎麼樣的?
實際上,如果將它的物件模型畫完整,那麼address會指向一塊空間:
我們也可以從彙編的層面上看:
知道了它的彙編,我們就能模擬實現求偏移量:
int tmp = 0;
d d;
tmp = *(
int*)((int
*)(*((int
*)(int
*)&d+2))+1);
printf("%d\n",tmp); //
12tmp = *(
int*)((int
*)(*(
int*)&d)+1);
printf("%d\n",tmp); //
20
C 繼承 二義性 虛繼承
繼承 子類擁有父類所有的成員變數和函式 子類是一種特殊的父類 子類可以當做父類的物件使用 子類可以擁有父類沒有的方法和屬性。class parent class child public parent int main 繼承的訪問控制 c 中的繼承方式會影響子類對外訪問屬性 1 看呼叫語句,是在類的...
python多繼承二義性
假如在多繼承中,父類a和父類b中有乙個同名的方法,子類呼叫的時候,呼叫哪個呢?class base object def test self print base class a base def test self print a class b base def test self print ...
C 多繼承的二義性
單繼承 派生類只從乙個基類派生 多繼承 派生類從多個基類派生 多重派生 有乙個基類派生出多個不同的派生類 多層派生 派生類又作為基類,繼續派生出新的類 多繼承可以看作是單繼承的擴充套件。所謂多繼承是指派生類具有多個基類,派生類與每個基類之間的關係仍可看作是乙個單繼承。多繼承下派生類的定義格式如下 c...