多重繼承
一個簡單的例子(非虛擬)
class Top
{
public: int a;
};
class Left : public Top
{
public: int b;
};
class Right : public Top
{
public: int c;
};
class Bottom : public Left, public Right
{
public: int d;
};
使用UML圖,我們可以把這個層次結(jié)構(gòu)表示為:

注意Top被繼承了兩次。這意味著類型Bottom的一個實例bottom將有兩個叫做a的元素(分別為bottom.Left::a 和 bottom.Right::a)。
Left、Right 和 Bottom在內(nèi)存中是如何布局的,讓我們先看一個簡單的例子。Left
和 Right擁有如下的結(jié)構(gòu):
| Left | Right |
|---|---|
| Top::a | Top::a |
| left::b | right::c |
請注意第一個屬性是從Top繼承下來的。這意味著在下面兩條語句后
Left* left = new Left();
Top* top = left;
Left 和 Top指向了同一地址,我們可以把Left Object當(dāng)成Top Object來使用(很明顯,Right與此也類似)。那Buttom呢?GCC的建議如下:
| Buttom |
|---|
| Left::Top::a |
| Left::b |
| Right::Top::a |
| Right::c |
| Bottom::d |
如果我們提升Bottom指針,會發(fā)生什么事呢?
Bottom* bottom = new Bottom();
Left* left = bottom;
這段代碼工作正常。我們可以把一個Bottom的對象當(dāng)作一個Left對象來使用,因為兩個類的內(nèi)存部局是一樣的。那么,如果將其提升為Right呢?會發(fā)生什么事?
Right* right = bottom;
為了執(zhí)行這條語句,我們需要判斷指針的值以便讓它指向Bottom中對應(yīng)的段。
| Bottom | |
|---|---|
| Left::Top::a | |
| Left::b | |
| right --> | Right::Top::a |
| Right::c | |
| Bottom::d |
經(jīng)過這一步,我們可以像操作正常Right對象一樣使用right指針訪問bottom。雖然,bottom與right現(xiàn)在指向兩個不同的內(nèi)存地址。出于完整性的緣故,思考一下執(zhí)行下面這條語句時會出現(xiàn)什么狀況。
Top* top = bottom;
是的,什么也沒有。這條語句是有歧義的:編譯器將會報錯。
error: `Top' is an ambiguous base of `Bottom'
兩種方式可以避免這樣的歧義
Top* topL = (Left*) bottom;
Top* topR = (Right*) bottom;
執(zhí)行這兩條語句后,topL 和 left會指向同樣的地址,topR 和 right也會指向同樣的地址。
虛擬繼承
為了避免重復(fù)繼承Top,我們必須虛擬繼承Top:
class Top
{
public: int a;
};
class Left : virtual public Top
{
public: int b;
};
class Right : virtual public Top
{
public: int c;
};
class Bottom : public Left, public Right
{
public: int d;
};
這就得到了如下的層次結(jié)構(gòu):

雖然從程序員的角度看,這也許更加的明顯和簡便,但從編譯器的角度看,這就變得非常的復(fù)雜。重新考慮下Bottom的布局,可能是:
| Bottom |
|---|
| Left::Top::a |
| Left::b |
| Right::c |
| Bottom::d |
這個布局的優(yōu)點是,布局的第一部分與Left的布局重疊了,這樣我們就可以很容易的通過一個Left指針訪問 Bottom類。
未完待續(xù)。。。
摘抄自 開源中國