菱形繼承的二義性和數據冗餘問題

什麼是繼承

繼承是面向對象複用的重要手段。通過繼承定義一個類,繼承是類型之間的關係建模,共享公有的東西,實現各自本質不同的東西。
繼承的方式分爲:
公有繼承、保護繼承、私有繼承
這裏寫圖片描述
總結:
1.基類的私有成員在派生類中是不能被訪問的,如果一些基類成員在類外不想被基類對象直接訪問,但需要在派生類中能訪問,就定義爲保護的。可以看出保護成員限定符是因爲繼承纔出現的。
2.public繼承是一個接口繼承,保持Is-a原則,每個父類可用的成員對子類也可用,因爲每個子類對象也是一個父類對象。也就是說,我就是你。我包含着你。
3.protected/privated繼承是一個實現繼承,基類的部分成員並未完全成爲子類藉口的一部分,
是has-a的關係原則,所以非特殊情況不會使用這兩種繼承關係,在絕大多數場景下使用的都是公有繼承。
4.不管是哪種繼承方式,在派生類內部都可以訪問積累的共有成員和保護成員,但是基類的私有成員在子類中不可見,就是不能訪問,但是也繼承下來了。
5.使用關鍵字class時默認的繼承方式是private,使用關鍵字struct時默認public,不過最好顯示的寫出繼承方式。
6.在實際運用中一般都是public繼承,很少有其他兩種

繼承的格式

class A
{
public:
int _a;
}
class B:public A
{
public:
int _b;
}

這種方式就是B公有繼承了A

繼承與轉換—賦值兼容規則–public繼承

1.子類對象可以賦值給父類對象(切片/切割)
2.父類對象不能賦值給子類對象
3.父類的指針/引用可以指向子類對象
4.子類的指針/引用不能指向父類對象(但是可以通過強制類型轉換實現,不過有危險性)

繼承體系中的作用域

1.在集成體系中基類和派生類都有獨立的作用域
2.子類和父類中有同名成員,子類成員將會屏蔽父類對成員的直接訪問。可以通過加上類名的方法進行調用(隱藏/重定義)
注意:在集成體系中最好不要定義同名的成員

派生類的默認成員函數

在繼承體系中,派生類如果沒有顯示定義這六個成員函數,編譯系統將會默認合成

單繼承和多繼承

1.單繼承就是一個子類只有一個直接的父類
2.多繼承就是一個子類有兩個或者兩個以上的直接的父類

菱形繼承

這裏寫圖片描述

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;
};

int main()
{
    D d;
    d._a = 10;//這裏調用成員的時候就會出現錯誤,對_a的訪問不明確

    d._b = 2;
    d._c = 3;
    d._d = 4;
    }

這裏寫圖片描述
要想解決這個問題也很簡單,只需要在調用的時候加上類的域名就好

d.B::_a = 10;
d.C::_a = 20;

運行結果如下圖
這裏寫圖片描述

菱形繼承的對象模型

這裏寫圖片描述

由此可以看出菱形繼承存在二義性問題,和數據冗餘問題,存在了兩份A類的成員

解決方案:

  1. 在調用對象的時候,指明類域就可以解決二義性問題
  2. 用虛繼承既可以解決數據冗餘的問題。
    虛繼承的格式,B類和C類虛擬繼承A
    格式:

class B:virtual public A
class C:virtual public A

進行改變後,上面的代碼再次運行就不會出現錯誤,因爲_a變爲公共的資源,具體可以打開監視窗口,發現只要改變_a的值,B和C類中的_a會同時改變
這裏寫圖片描述

虛繼承的對象模型如下圖

這裏寫圖片描述
在內存中具體的圖示(代碼就是上面的那個)
這裏寫圖片描述
總結:
1.虛繼承解決了在菱形繼承中子類對象包含多分父類對象的數據冗餘和浪費空間的問題
2.虛繼承體系看起來很複雜,在實際應用中我們通常不會定義如此複雜的集成體系。一般不到萬不得已不要使用菱形虛擬繼承,因爲它解決數據冗餘問題的同時也帶來了性能上的損耗

這個是我之前寫的繼承,更加詳細一點
[繼承] (http://blog.csdn.net/chan0311/article/details/69791225)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章