1. 程式人生 > >c++中的菱形繼承與虛擬菱形繼承

c++中的菱形繼承與虛擬菱形繼承

c++中的繼承關係分為單繼承和多繼承

  • 單繼承:一個派生類只有一個基類
  • 多繼承:一個派生類不止有一個基類

在多繼承的過程成容易造成二義性問題。

菱形繼承是多繼承中的一種複雜的情況。
在這裡插入圖片描述
菱形繼承的有兩個問題:

  • 二義性:當使用A的資料時無法確定繼承自B 還是 C
  • 資料冗餘:類D中會有兩份類A的資料

先看一個菱形繼承的例子

#include <iostream>
using namespace std;

class A{

public:
	int _a = 1;
};

class B : public A{

public:
	int _b = 2;
};

class C : public A{
public:
	
	int _c = 3;
};

class D : public B, public C {
public:

	int _d = 4;
};

int main(){
	D d1;
	//d1._a = 100;

	d1.B::_a = 100;  //可以顯示的指定訪問那一個類中的成員,解決了二義性問題
	d1.C::_a = 101;

}

在這裡插入圖片描述
D中最後會出現兩份_a,這就造成了資料冗餘,為了解決資料冗餘的問題,使用了虛擬繼承

l菱形虛擬繼承

通過菱形的虛擬繼承,可以使該多繼承體系中只有一份公共的A中的資料

//虛擬繼承通過virtual關鍵字實現
class A{

public:
	int _a = 1;
};

class B : virtual public A{

public:
	int _b = 2;
};

class C : virtual public A{
public:
	
	int _c = 3;
};

class D : public B, public C {
public:

	int _d = 4;
};

在虛擬繼承中通過使用虛基表,來實現虛擬繼承不對原繼承體系的原本繼承關係造成影響。儘管現在只有一份公共的A,但原來的繼承關係並不發生改變,只要繼承了A都了能訪問A的資料,我們通過它們的記憶體模型來看
在這裡插入圖片描述
通過虛基表我們可以發現,此時物件d1中只有一個A中的資料_a,不再出現冗餘的兩份_a,而原來存放繼承自B中的_a的位置以及繼承自C中的_a的位置,此時存放的是一個地址,找到該地址,發現其中存放的是數,而這個數就是一個偏移量,表示此時B和C的位置到達公共部分_a的偏移量。這兩個地址叫做虛基表指標,而指向的就是虛基表,這兩個虛基表中存放的是偏移量。

繼承關係和組合關係

多繼承體現了c++的語法複雜性,算是c++的一個設計缺陷,因為多繼承的關係,所以有了菱形繼承,從而出現了虛擬繼承。從而導致了複雜度的上升,所以儘量少的使用多繼承。

組合
class Legs{
 protected:
 size_t _size = 17; // 腿長

 };


class Person{
 protected:

 string _name ;//姓名 
 Legs _leg;
 };

繼承體現的是設計層次結構中的複用,而還有一種物件間的服用關係,就是組合。

  • 繼承表明了每一個派生類物件都是一個基類物件,是一種has-a的關係。

  • 組合關係表示,如果B組合A,表明每一個B物件中都有一個A物件,是一種is-a的關係。
    -優先使用組合關係,而不是繼承關係,表明了一個物件是組成另一個物件的一部分。

  • 派生類與基類之間的關係又稱為白箱複用,白箱是指可視性而言,在繼承關係中基類內部的細節對於派生類來說是可見的,基類在一定程度上破壞了封裝,基類的改變在很大程度上會影響派生類。所以繼承關心間的關聯性很強,耦合度很高。

  • 組合關係是一種黑箱複用,被組合的類細節在另一個類中是不不可見的,被組合的類只需要提供良好的介面即可。組合之間的關係的依賴程度低,耦合度低,更能保證封裝的完好性。

  • 面向物件的設計中傾向於高內聚,低耦合的設計,所以繼承和組合關係間,優先使用自合