32.Make sure public inheritance models "is-a".
所謂的最佳設計,取決於系統希望做什麼事,包括現在與未來。
需要解決的問題:其中關於兩個assert都通過的地方有些疑惑。
總結:
“public繼承”意味著is-a。適用於base classes身上的每一件事情一定也適用於derived classes身上,因為每一個derived class對象也都是一個base class對象。
33.Avoid hiding inherited names.
總結:
第一,derived classes內的名稱會遮掩base class內的名稱。在public繼承下從來沒有人希望如此。
第二,為了讓被遮掩的名稱再見天日,可使用using聲明式或轉交函數(forwarding functions)。
批注:
轉交函數就是在子類中的成員函數中跳轉到父類的成員函數。僅僅是加了一層包裝。
34. Differentiate between inheritance of interface and inheritance of implementation.
1)聲明一個pure virtual函數的目的是為了讓derived classes只繼承函數接口。
2)聲明簡樸的(非純)impure virtual函數的目的,是讓derived classes繼承該函數的接口和缺省實現。
3)聲明non-virtual函數的目的是為了令derived classes繼承函數的接口及一份強制性實現。
總結:
第一,接口繼承和實現繼承不同。在public繼承之下,derived classes總是繼承base class的接口。
第二,pure virtual 函數只具體制定接口繼承。
第三,簡樸的(非純)impure virtual函數具體指定接口繼承及缺省實現繼承。
第四,non-virtual函數具體制定接口繼承以及強制性實現繼承。
35.Consider alternatives to virtual functions.
令客戶通過public non-virtual成員函數間接調用private virtual函數,稱為non-virtual interface(NVI)手法。它是所謂Template Method設計模式(與C++ templates並無關聯)的一個獨特表現形式。
1)使用non-virtual interface(NVI)手法,那是Template Method設計模式的一種特殊形式。它以public non-virtual 成員函數包裹較低訪問性(private或protected)的virtual函數。
2)將virtual函數替換為“函數指針成員變量”,這是Strategy設計模式的一種分解表現形式。
3)以tr1::function成員變量替換virtual函數,因而允許使用任何可調用物(callable entity)搭配一個兼容於需求的簽名式。這也是Strategy設計模式的某種形式。
4)將繼承體系內的virtual函數替換為另一個繼承體系內的virtual函數。這是Strategy設計模式的傳統實現手法。
總結:
第一,virtual函數的替代方案包括NVI手法及Strategy設計模式的多種形式。NVI手法自身是一個特殊形式的Template Method設計模式。
第二,將機能從成員函數移到class外部函數,帶來的一個缺點是,非成員函數無法訪問class的non-public成員。
第三,tr1::function對象的行為就像一般函數指針。這樣的對象可接納“與給定之目標簽名式(target signature)兼容”的所有可調用物(callable entities)。
批注:該條款主要介紹了針對virtual函數實現功能的集中實現方式,主要是分為兩個方面,一個方面是NVI,一個方面是設計模式中的Strategy的幾種實現方式。
36. Never redefine an inherited non-virtual function.
總結:
絕對不要重新定義繼承而來的non-virtual函數。
批注:
主要是因為如果你這麼做了,首先,你破壞了non-virtual函數應該被完全繼承的初衷;其次,這樣做會導致混亂。所以這是一個不合理的情況,要不聲明為virtual,要不就不要重新定義。否則這樣帶來的混亂是對象執行成員函數的時候,取決於它們的聲明類型,而不是本身自己所指向的類型。
37. Never redefine a functions's inherited default parameter value.
對象的所謂靜態類型(static type),就是它在程序中被聲明時所采用的類型。
對象的所謂動態類型(dynamic type),就是指“目前所指對象的類型”。
總結:
絕對不要重新定義一個繼承而來的缺省參數數值,因為缺省參數數值都是靜態綁定,而virtual函數---你唯一應該覆寫的東西---卻是動態綁定。
批注:
由此可見很大一部分問題都是由於動態綁定和靜態綁定的原因造成的。由此可類推到virtual函數以及non-virtual函數。
38. Model "has-a" or "is-implemented-in-terms-of" through composition.
總結:
第一,復合(composition)的意義和public繼承完全不同。
第二,在應用域(application domain),復合意味著has-a(有一個)。在實現域(implementation domain),復合意味is-implemented-in-terms-of(根據某物實現出)。
39. Use private inheritance judiciously.
復合和private繼承都意味is-implemented-in-terms-of,但復合比較容易理解,所以無論什麼時候,只要可以,你還是應該選擇復合。
總結:
第一,private繼承意味is-implemented-in-term-of(根據某物實現出)。它通常比復合(composition)的級別低。但是當derived class需要訪問protected base class的成員,或需要重新定義繼承而來的virtual函數時,這麼設計是合理的。
第二,和復合(composition)不同,private繼承可以造成empty base最優化。這對致力於“對象尺寸最小化”的程序庫開發者而言,可能很重要。
批注:
主要關注了private繼承下的情況。
40. Use multiple inheritance judiciously.
總結:
第一,多重繼承比單一繼承復雜。它可能導致新的歧義性,以及對virtual繼承的需要。
第二,virtual繼承會增加大小、速度、初始化(及賦值)復雜度等等成本。如果virtual base class不帶任何數據,將是最具實用價值的情況。
第三,多重繼承的確有正當用途。其中一個情節涉及“public繼承某個Interface class”和“private繼承某個協助實現的class”的兩組結合。