读书笔记《Effective C++》条款40:明智而审慎地使用多重继承
一旦涉及多重繼承,C++社群便分為兩個基本陣營。一派認(rèn)為如果單一繼承是好的,多重繼承一定更好。另一派主張,單一繼承是好的,但多重繼承不值得使用。
最先需要認(rèn)清的一件事是,當(dāng)用到多重繼承,程序有可能從一個以上的base class繼承相同名稱(如函數(shù)、typedef等等),那會導(dǎo)致較多的歧義。例如:
class BorrowableItem { public:void checkOut(); };class ElectronGadget { private:bool checkOut() const; };class MP3Player : public BorrowableItem, public ElectronGadget {};MP3Player mp; mp.checkOut();//歧義:調(diào)用的是哪個checkOut? 注意此例中對checkOut的調(diào)用是有歧義的,即使兩個函數(shù)之中只有一個可取用(BorrowableItem內(nèi)的checkOut是public,ElectronicGadget內(nèi)的卻是private)。這與C++用來解析重載函數(shù)調(diào)用的規(guī)則相符:在看到是否有個函數(shù)可取用之前,C++首先確認(rèn)這個函數(shù)對此調(diào)用之言是最佳匹配。找出最佳匹配函數(shù)后才檢驗其可取性。本例中的兩個checkOut函數(shù)有相同的匹配程度,沒有所謂最佳匹配。因此ElectronicGadget::checkOut的可取用性也就從未被編譯器審查。為了解決這個歧義,必須明確指出要調(diào)用哪一個base class內(nèi)的函數(shù):
mp.BorrowableItem::checkOut();多重繼承的意思是繼承一個以上的base class,但這些base class并不常在繼承體系中又有更高級的base class,那會導(dǎo)致要命的“鉆石型多重繼承”:
class File {}; class InputFile : public File {}; class OutputFile : public File {}; class IOFile : public InputFile, public OutputFile {};任何時候如果有一個繼承體系而其中某個base class和某個derived class之間有一條以上的相同路線,就必須面對這樣一個問題:是否打算讓base class內(nèi)的成員變量經(jīng)由每一條路徑被復(fù)制?假設(shè)File class有個成員變量fileName,那么IOFile內(nèi)該有多少筆這個名稱的數(shù)據(jù)呢?從某個角度說,IOFile從其每一個base class繼承一份,所以其對象內(nèi)應(yīng)該有兩份fileName成員變量。但從另一個角度說,IOFile對象只該有一個文件名稱,所以它繼承自兩個base class而來的fileName不該重復(fù)。C++對這兩個方案都支持——雖然其缺省做法是執(zhí)行復(fù)制。如果那不是你想要的,你必須令那個帶有此數(shù)據(jù)的class(也就是File class)成為一個virtual base class。為了這樣做,必須令所有直接繼承自它的class采用“virtual繼承”:
class File {}; class InputFile : virtual public File {}; class OutputFile : virtual public File {}; class IOFile : public InputFile, public OutputFile {};從正確行為的觀點看,public繼承應(yīng)該總是virtual。但是為了避免繼承得來的成員變量重復(fù),編譯器必須提供若干幕后工作,而其后果是:使用virtual繼承的那些class所產(chǎn)生的對象往往比使用non-virtual繼承的class體積大,訪問virtual base class的成員變量時,也比訪問non-virtual base class的成員變量速度慢。種種細(xì)節(jié)因編譯器不同而異,但基本重點很清楚:你得為virtual繼承付出代價。
virtual繼承的成本還包括其他方面。支配“virtual base class初始化”的規(guī)則比其non-virtual base的情況遠(yuǎn)為復(fù)雜且不直觀。virtual base的初始化責(zé)任是有繼承體系中的最低層class負(fù)責(zé)。
忠告很簡單:第一,非必要不使用virtual base。平常請使用non-virtual繼承。第二,如果你必須使用virtual base class,盡可能避免在其中放置數(shù)據(jù)。這么一來就不需要擔(dān)心這些class身上的初始化(和賦值)所帶來的詭異事情了。Java和.Net的Interface值得注意,它在許多方面兼容于C++的virtual base class,而且也不允許含有任何數(shù)據(jù)。
要點:
1.多重繼承比單一繼承復(fù)雜。它可能導(dǎo)致新的歧義性,以及對virtual繼承的需要。
2.virtual繼承會增加大小、速度、初始化(及賦值)復(fù)雜度等等成本。如果virtual base class不帶任何數(shù)組,將是最具實用價值的情況。
3.多重繼承的確有正當(dāng)用途。其中一個情節(jié)涉及“public繼承某個interface class”和“private繼承某個協(xié)助實現(xiàn)的class”的兩相組合。
總結(jié)
以上是生活随笔為你收集整理的读书笔记《Effective C++》条款40:明智而审慎地使用多重继承的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 李宏毅 机器学习 2016 秋:5、Cl
- 下一篇: jlh吃水果(C++)