繼承與面向對象設計

32:make sure public inheritance models “is-a”
public繼承意味着is-a,所有能在基類對象上執行的操作,派生類對象也能執行。派生類除了是基類的一種,還可以有自己獨特的操作,這就決定了基類不能是派生類。
classes之間的關係:
is-a(是一個)、has-a(有一個)、is-implemented-in-terms-of(根據某物實現出)
在這裏插入圖片描述33:avoiding hiding inherited names
當派生類繼承基類後,在派生類中可以訪問基類成員的原理:派生類的作用域被嵌套在基類作用域內。
C++中的名稱查找:逐作用域查找名稱,從local作用域、派生類作用域、基類作用域、全局作用域。
在這裏插入圖片描述下述場景:public繼承,但已然違反了public繼承含義:基類支持的操作派生類也支持。由於在派生類的作用域內發生了名稱遮蓋,將導致派生類對象無法執行基類提供的部分操作。
在這裏插入圖片描述修正如下:使用using聲明式,使得在在派生類作用域內基類名稱也可見
在這裏插入圖片描述名稱遮蔽也並不是只有壞處:在private繼承下,可以幫助我們實現,只繼承某個特殊的函數,在派生類的作用域下,只有新聲明的名稱可見,基類同名的名稱都無法使用。
在這裏插入圖片描述
34:differentiate between inheritance of interface and inheritance of implementation
public繼承:一定是保證了函數接口繼承
public繼承進一步細化可分爲:函數接口繼承、函數實現繼承
C++爲類的設計者提供了三種繼承方式:派生類僅繼承函數接口;派生類繼承函數接口和函數缺省實現(允許派生類覆寫函數實現);派生類繼承函數接口和強制實現(不允許覆寫函數實現)
純虛函數:派生類僅從基類繼承函數接口,派生類必須重新聲明該接口,基類要求派生類繼承此接口,但不管派生類如何實現。純虛函數可以有定義,使用純虛函數的定義,可以幫助我們實現更安全的缺省實現,以免有些粗心的派生類設計者使用不當。
在這裏插入圖片描述impure virtual function:使派生類繼承函數接口和缺省實現。派生類可以重寫缺省實現,做出特殊的行爲,也可以就使用基類提供的缺省實現。缺省實現自動提供給每一個派生類,但現在我們改變一下策略:有一個缺省實現,但不隱式繼承給派生類,必須派生類顯示調用該缺省實現。利用pure virtual函數和其實現達到這一目的:
在這裏插入圖片描述non-virtual 函數:繼承函數接口和強制實現,無論什麼派生類均使用基類提供的實現,在派生類中不應該被重新定義。

在這裏插入圖片描述36:never redefine an inherited non-virtual functions
公有繼承下,non-virtual 函數的不變性高於特異性
如果在派生類定義了non-virtual函數,將導致一個對象可能由於使用靜態類型(指針、引用)的不同而導致同一個對象調用同一函數卻產生不同的行爲。
理論上,如果派生類是一個基類,基類的操作也適用於派生類對象,基類定下的non-virtual 函數一定是使用於所有的派生類,如果你認爲派生類的non-virtual函數需要重寫,那說明public繼承不適合,不滿足is-a關係。或是基類的設計不夠準確,完善基類的設計。

37:never redefine a function inherited default parameter value.
繼承一個帶有默認參數的虛函數可能會有什麼問題?當使用指針、引用調用虛函數時,默認參數均使用基類所定義的,就算重定義了參數值,基類指針調用函數時也不會使用派生類定義的參數。但我們希望得到一種一致性:無論是派生類類型還是基類類型指針(引用),調用同一個函數時,函數默認參數都是一致的。
虛函數的調用關係動態綁定、默認參數的確定是靜態綁定的
對象聲明時的類型是靜態類型
在這裏插入圖片描述當前指針實際指向的類型是動態類型,而動態類型將決定執行何種操作。如果基類有默認參數,基類指針指向派生類對象,那麼將調用派生類對象,但使用基類的默認參數,C++將默認參數的確定放在編譯期間可以保證運行效率。
一旦採取這樣的寫法,很容易帶來混亂,靜態類型的不同將導致可能即繼承基類的默認參數,也可能不繼承默認參數,太不友好了,如何避免這樣的設計?

在這裏插入圖片描述1、方案一:問題出在基類虛函數的默認參數和派生類虛函數默認參數不一致,那麼就讓他們一致。但是一旦修改基類的默認參數,就得修改所有派生類的默認參數。這絕對不是好的方法。
在這裏插入圖片描述
2、使用non-virtual的函數,使其帶有默認參數,在其內部調用virtual函數。如果需要修改默認參數值就在non-virtual函數修改即可。
在這裏插入圖片描述38:model “has-a" or “is-implemented-in -terms -of” through composition.
複合是類型之間的一種關係,一個類型內含多個其他類型
通過複合來實現has-a和根據某物實現出
has-a:
在這裏插入圖片描述根據某物實現出:
在這裏插入圖片描述在這裏插入圖片描述39:use private inheritance judiciously
私有繼承效果等效於複合,利用已有的實現做些什麼,繼承實現,不繼承接口。
根據某物實現出的概念,採用public繼承也是可以的,但是這與公有繼承的觀念違背,不再有派生類是一個子類。派生類對象不能在去調用父類的接口。因此,遵從接口容易被使用,不易被誤用的指導原則,不能採取public繼承。
儘可能使用複合,希望使用到protected成員、重寫虛函數,才考慮使用private inheritance。
在這裏插入圖片描述在這裏插入圖片描述如果我們希望Widget的派生類不能重定義onTick()(基類的私有virtual函數成員可以在派生類中被重定義,調用發生在基類,定義發生在各個派生類中,兩個過程並不互相沖突)
阻止派生類定義虛函數:
定義一個繼承Time的特殊類,並作爲Widget的私有成員,那麼所有Widget的派生類無法訪問到私有成員,更無法重寫其虛函數。
在這裏插入圖片描述幫我們解決編譯依存的問題:
採用private繼承,一定要求基類的定義可見,爲此需要加入頭文件;但此時,如果我們把WidgetTimer類定義到其他文件中,這裏使用指向該類型的指針作爲私有成員。
在這裏插入圖片描述
40:use multiple inheritance judiciously
多繼承的問題,多個父類就可能帶來名稱衝突
在這裏插入圖片描述多繼承中解析函數調用與函數重載過程類似:找到最佳匹配後才判斷函數是否可用,在這裏,兩個父類的chekout函數一樣好,雖然有一個是私有的,不可用,但編譯器在尋找最佳匹配時就卡殼了。爲解決這個問題,寫出來具體的基類。
在這裏插入圖片描述問題2:當多重繼承的多個直接基類有着相同的基類,最最根本的成員在派生類中有幾份呢?

發佈了86 篇原創文章 · 獲贊 9 · 訪問量 9459
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章