[原創(chuàng)文章歡迎轉(zhuǎn)載,但請(qǐng)保留作者信息]
Justin 于 2010-01-21
Scott
開(kāi)篇就直接說(shuō)開(kāi)了:
C++
陣營(yíng)中關(guān)于多重繼承
(Multiple Inheritance, MI)
分成了兩派,一派認(rèn)為多重繼承比單一繼承好,另外一邊則認(rèn)為弊大于利。
所以本課的內(nèi)容就是說(shuō)說(shuō)
MI
的優(yōu)與劣。
MI
的第一個(gè)問(wèn)題就是名字沖突,
最經(jīng)典的例子就是鉆石問(wèn)題
(diamond problem)。
設(shè)想
A
中有一個(gè)函數(shù)叫做
GetName()
,
B
和
C
中都將有這一函數(shù)成員,這個(gè)時(shí)候
D
::
GetName()
的真正實(shí)現(xiàn)是來(lái)自
B
的還是
C
的呢?二義性出現(xiàn)了
(ambiguity)
。
不過(guò)如果真的發(fā)生了這種情況,要解決的方法也不是沒(méi)有,可以這樣做:D?d;
d.B::GetName();?//Calling?B's?implementation
嗯,很容易理解。
另外一個(gè)高階一點(diǎn)的方法叫做虛繼承
(virtual inheritance)
。對(duì)于在虛擬繼承中的父類(lèi),其中的成員都保證不會(huì)在后面的子類(lèi)中出現(xiàn)二義現(xiàn)象
(ambiguity)
。似乎是專(zhuān)門(mén)為了
MI
才整出來(lái)的,汗
……
例子還是已前面的鉆石問(wèn)題:class?A
{
???public:
??????void?GetName();
//..
};
class?B?:?virtual?public?A
{
//..
};
class?C?:?virtual?public?A
{
//..
};
class?D?:?public?B,?public?C
{
//..
}
D?d;
d.GetName();?//there?is?no?ambiguity?here.
但是虛繼承不是沒(méi)有代價(jià)的,大師說(shuō)這種技術(shù)會(huì)使得最終代碼變得更大,訪問(wèn)虛擬繼承中的父類(lèi)成員也會(huì)變得更慢一些。
這個(gè)也不難理解。和空間換時(shí)間一樣,和不給牛吃草牛就不干活一樣。
(
另外的一個(gè)代價(jià)我還沒(méi)能完全理解透徹:書(shū)上說(shuō)因?yàn)樘摾^承中基類(lèi)的初始化是由繼承關(guān)系中最底層的子類(lèi)負(fù)責(zé)的,因此對(duì)于這些最底下的
“
嫡孫
”
類(lèi)來(lái)說(shuō),就不是那么方便了
)
于是大師建議只有在必要的時(shí)候才使用虛繼承,而在虛繼承中的基類(lèi)里也不要放置數(shù)據(jù)成員,這樣就不用擔(dān)心初始化的問(wèn)題了。
不過(guò)存在就是合理,還是有需要用到
MI
的時(shí)候。一個(gè)在書(shū)中提到的使用
MI
的情形是:當(dāng)需要從一個(gè)類(lèi)
AClass
中繼承接口,又需要從另外一個(gè)類(lèi)
BClass
中繼承實(shí)現(xiàn)細(xì)節(jié)時(shí),就可以考慮在公有繼承
AClass
的同時(shí)又私有繼承
BClass
。道理大致就是這樣,就不編造程序畫(huà)蛇添足了。
總結(jié)一下:
MI
比
SI(Single
Inheritance)
要復(fù)雜容易出錯(cuò)
(
比如說(shuō)鉆石問(wèn)題
)
,即使可以用虛繼承來(lái)解決鉆石問(wèn)題,但是其帶來(lái)的代碼體積增大,訪問(wèn)效率下降以及初始化問(wèn)題還是不能忽視的。最后話(huà)說(shuō)回來(lái),需要用到
MI
的時(shí)候,小心點(diǎn)用便是
@#
¥
%
【參考】
http://en.wikipedia.org/wiki/Virtual_inheritance