1. 程式人生 > >菱形繼承問題以及虛擬繼承探究

菱形繼承問題以及虛擬繼承探究

在前邊的部落格中我提到過菱形繼承的問題,也給出了幾種解決菱形繼承的方法

但是給的方法沒有說明原因,這篇部落格將會從記憶體儲存模型角度,以及虛繼承為什麼可以使解決菱形繼承的問題進行闡述與證明。

 所謂菱形繼承就是指類B,類C同時繼承A,且D同時繼承B,C,在A具有成員函式的情況下,在D類例項化出來的物件中對A的成員函式進行直接呼叫會發生二義性的問題,也就是編譯器不知道是尋找B繼承的成員函式還是A繼承的成員函式。

但是在B,C繼承A的時候,加上虛繼承則不會發生錯誤

但是為什麼虛繼承會使得這種問題得以解決呢?

原因是虛繼承其實改變了傳統的類繼承的空間結構模型,可以在記憶體顯示視窗看到

為了使得記憶體易於找到,在A中放入幾個值,則有以下程式碼:

#include <iostream>
using namespace std;

class A
{
public:
	A()
		:a(1)
	{}
	void Print(void) {
		cout << "hello" << endl;
	}

	int a;

};

class B:virtual public A{
public:
	B() 
		:b(2)
	{}
	int b;
};

class C:virtual public A{
public:
	C()
		:c(3)
	{}
	int c;
};

class D:public B, public C{
public:
	D() 
		:d(4)
	{}
	int d;
};

int main()
{
	D d;
	d.Print();
	return 0;
}

執行起來記憶體分佈結構為:

顯而易見的結果是:

最底部為A的成員,有A的獨立資料,在A之上存放D的獨有資料,D之上存放著C的獨有資料的同時有一個類似地址的十六進位制值,同時B也有一個類似地址的值。

若是地址,我們可以在記憶體中尋找該地址所對應的是什麼內容,於是我們有以下訪問地址操作:

查詢地址0x00 2d 9b 50 與 0x00 2d 9b 5c所對應的值

注:為何我在查詢地址的過程中將地址倆倆倒過來寫?因為由於我所用的計算機是小端機器,低地址存高位,所以在查記憶體時需要反過來寫。

查詢結果為:14和0c,那麼這兩個數字指向的地址值到底是代表什麼意思呢?

其實給它們分別為記錄對A的偏移量。

在虛繼承的過程中,派生類獨有資料之前都會有一個指標,這個指標指向一張名為“虛基表”的資料結構,該資料結構記錄了許多資訊,其中就會包括它們對於父類的偏移量,在我們D進行A成員函式操作的時候,找到地址,會尋找到A的成員函式地址,在進行Call呼叫,這樣就徹底的解決了菱形繼承的問題。