文章目錄
條款32.確定你的public繼承塑模出is-a關係
public繼承:意味着 is - a 。適用於base classes身上的每一件事情一定也適用於derived classes身上,因爲每一個derived class對象也都是base class對象。
條款33.避免遮掩繼承而來的名稱
派生類內的名稱會遮掩基類中的名稱,爲了防止基類的名稱被遮掩,可以使用using聲明式或轉角函數(實現部分繼承)
有如下調用:
說明:此時派生類中只能調用無參的Derived::mf1
和Derived::mf3
爲了讓派生類的對象繼承基類中的重載函數,可以使用using 進行聲明
如下調用就完全沒有問題:
繼承基類中的部分函數:藉助轉交函數
條款34.區分接口繼承和實現繼承
- 成員函數的接口總是會被繼承
- 聲明一個純虛函數的目的是讓派生類只繼承函數接口
- 聲明非純虛函數具體指定接口繼承及缺省實現繼承
- 聲明非虛函數的目的是令派生類繼承函數的接口以及一份強制性的實現
簡單解釋:非純虛函數具體指定接口及缺省實現繼承,
如果想在派生類中有的派生類不需要缺省行爲,有的派生類需要缺省行爲,可以將函數的聲明部分指定爲純虛函數,函數的定義部分實現缺省時的一些行爲,對於需要缺省行爲的派生類直接調用基類中的純虛函數的定義部分就可以。
條款35.考慮virtual函數以外的其他選擇
1.藉由non-virtual interface (NIV)手法實現 模板方法模式
2.藉由函數指針實現策略模式
使用這種方式的好處:
3.藉由function完成策略模式
說明:function<int (const GameCharacter&)>
所代表的函數的參數類型是一個GameCharacter
類型的引用,返回值的類型是int
該設計方法使得函數調用時有驚人的彈性:
4.傳統的策略模式
條款36.絕不重新定義繼承而來的non-virtual函數
條款37.絕不重新定義繼承而來的缺省參數值
如果在基類中已經給虛函數中的形參列表中的參數默認值,在派生類中不要再重新給該形參不同的默認值,在動態綁定下該虛函數會從基類中繼承缺省參數值。
因爲對象對應的虛函數是動態綁定,而虛函數的形參是靜態綁定。
設想一下:如果基類中的默認參數值發生了改變,其所有派生類中的默認形參都要改變,如何讓這種形式變得更加便捷呢?借用上一條款中的非虛函數接口(NVI)形式
派生類中只需定義私有的doDraw
函數即可
條款38.通過複合塑模出has-a或“根據某物實現出”
複合有兩種含義:“has-a”和“is-implemented-in-terms-of”(根據某物實現出)
- has-a:在應用域進行復合,某個類的實現需要其他類的對象作爲成員變量
- is-implemented-in-terms-of:在實現域進行復合,某個類的實現需要大量的依靠另一個類的方法
在應用域複合:
在實現域符合:
條款39.明智而審慎地使用private繼承
注意下面這種情況:當出現private繼承時,出現基類的指針或者是引用指向派生類的對象的時候,編譯器不會將派生類的對象轉化爲基類的對象,會出現錯誤
總結:
- private繼承意味着is-implemented-in-terms of(根據某物實現出)。它通常比複合的級別低。但是當派生類需要訪問基類的保護成員時或者需要重新定義繼承而來的虛函數時,這麼設計合理
條款40.明智而審慎的使用多重繼承
多重繼承會出現較多的歧義:
正確的調用方式:指明調用的是哪一個基類的函數
另一種多重繼承的方式:
爲了避免基類File
的成員在IOFile
中有兩份,採用虛繼承的方式
總結:
- 多重繼承比單一繼承複雜。可能導致新的歧義性,以及對虛繼承的需要
- 虛繼承會增加大小、速度、初始化的複雜度和成本
- 多繼承一般用於:以公有的方式繼承某個抽象基類,以私有的方式繼承某個協助實現的基類