多重繼承
一個簡單的例子(非虛擬)
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當成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的對象當作一個Left對象來使用,因為兩個類的內(nèi)存部局是一樣的蹂喻。那么葱椭,如果將其提升為Right呢?會發(fā)生什么事口四?
Right* right = bottom;
為了執(zhí)行這條語句孵运,我們需要判斷指針的值以便讓它指向Bottom中對應的段。
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也會指向同樣的地址。
虛擬繼承
為了避免重復繼承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):
雖然從程序員的角度看绳瘟,這也許更加的明顯和簡便,但從編譯器的角度看姿骏,這就變得非常的復雜糖声。重新考慮下Bottom的布局,可能是:
Bottom |
---|
Left::Top::a |
Left::b |
Right::c |
Bottom::d |
這個布局的優(yōu)點是,布局的第一部分與Left的布局重疊了蘸泻,這樣我們就可以很容易的通過一個Left指針訪問 Bottom類琉苇。
未完待續(xù)。悦施。并扇。
摘抄自 開源中國