C++菱形繼承與虛擬繼承

C++的繼承中有一種複雜的繼承方式,這就是菱形繼承。

菱形繼承

(1)什麼是菱形繼承?

假設現在有四個類,分別是A、B、C、D四個類。如果B類和C類同時繼承於A類,並且D類又同時繼承於B類和C類,那麼這四個類之間的關係就叫做菱形繼承。可以用下面的圖來表示。
在這裏插入圖片描述

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

1. 爲什麼說會存在數據冗餘?
首先B類和C類都繼承了A類,所以B類和C類中都一定包含A類的成員變量a,也就是說B類中有一個int a,同樣C類中也有一個int a。那麼當D類繼承B類和C類後,那麼在D類中一定包含兩個int a,其中一個來自B類,另外一個來自C類。這就是所謂的數據冗餘。
2. 爲什麼說會造成二義性?
根據上面的分析,D類中存在兩個int a。所以當我們想要用D類的對象訪問int a時,那麼編譯器就不知道究竟訪問的是哪一個a。究竟訪問的是從B類繼承的a,還是訪問的是從C類繼承的a,這就是所謂的二義性。當然對於二義性我們可以用“類名::”的方式來解決。如下所示:

cout<<d1.B::a<<endl;   //這訪問的繼承於B類的a(其中d1是D類的對象)
cout<<d1.C::a<<endl;   //這訪問的繼承於C類的a(其中d1是D類的對象)

但是這樣的辦法卻解決不了數據冗餘的問題,所以就有了虛擬繼承。

虛擬繼承

虛擬繼承不僅解決了菱形繼承中的數據冗餘問題,還解決了二義性問題。

(1)虛擬繼承的方式

我在上面的代碼上進行一些修改,這樣可以增加對比性。

class A
{
   public:
		int a;
};
class B:virtual public A       //僅僅是比之前多加了一個關鍵字virtual
{
   public:
		int b;
};
class C:virtual public A      //僅僅是比之前多加了一個關鍵字virtual
{
   public:
		int c;
};
class D:public B,public C
{
   public:
		int d;
};

解析:
在上述代碼中,當B類和C類從A類派生出來時,我們使用virtual關鍵字將A類聲明爲虛基類。這樣從B類和C類派生詞來的子類D,在繼承時只是繼承了A類的一份,也就是隻保留了A類的成員變量a一份。於是便很好的解決了數據冗餘和二義性問題。

(2)虛擬繼承的原理

我結合下面一段代碼來具體解釋。

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;
};
int main()
{
	 D d;
	 d.B::_a = 1;
	 d.C::_a = 2;
	 d._b = 3;
	 d._c = 4;
	 d._d = 5;
	 return 0;
}

首先看下面這張圖:這張圖片是沒有使用虛擬繼承時候的內存情況
在這裏插入圖片描述
在沒有使用虛擬繼承時,在內存中從上到下分別存儲了從B類中繼承下來的成員(包括int _a 和int _b)、從C類中繼承下來的成員(包括int _a和int _c)以及D類中新增的成員(int _d)。

下面這張圖是使用了虛擬繼承的內存狀態圖:
在這裏插入圖片描述
使用了虛擬繼承之後,在B類和C類的同名成員的位置發生了改變,由原來的具體的值變成了一個地址,這其實就是一個指針,我們把這個指針叫做虛基表指針。而這個地址表示的就是一個虛基表的首地址。而在虛基表中存放的是內容是:當前位置相當於公共父類成員的偏移量。
舉個例子:
假設現在要訪問D類中的成員變量int a。那麼編譯器首先會找到該類對象的內存空間,然後在此空間尋找a的位置,但是當他找到a的位置時,發現原本應該是具體數值結果變成了一個地址,也就是說原本的值被一個虛基表指針所取代。然後編譯器會根據這個虛基表指針找到這個虛基表的首地址,然後根據虛基表中的內容,最後才找到實際上a的具體地址,然後訪問a

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