Effective C++讀書筆記---繼承與面向對象設計

如果你瞭解C++各種特性的意義,你會發現,你對OOP的看法改變了。它不再是一項用來劃分語言特性的儀典,而是可以讓通過它說出你對軟件系統的想法。一旦你知道該通過它說些什麼,轉移至C++世界也就不再是可怕的高要求了
三十二、條款32-確定你的public繼承塑模出is-a關係
1.is-a並非唯一存在於classes之間的關係。別兩個常見的關係是has-a(有一個)和is-implemented-in-terms-of(根據某物實現出)。這些關係將在條款38和39討論。將上述這些重要的相互關係中的任何一個誤塑爲is-a而造成的錯誤設計,在C++中並不罕見,所以你應該確定你確實瞭解這些個“classes相互關係”之間的差異,並知道如何在C++中最好地塑造它們
2.“public繼承”意味is-a。適用於base classes身上的每一件事情一定也適用於derived classes身上,因爲每一個derived class對象也都是一個base class對象
三十三、條款33-避免遮掩繼承而來的名稱
1.derived classes內的名稱會遮掩base classes內的名稱。在public繼承下從來沒有人希望如此
2.爲了讓被遮掩的名稱再見天日,可使用using聲明式或轉交函數(forwarding functions)
三十四、條款34-區分接口繼承和實現繼承
1.接口繼承和實現繼承不同。在public繼承之下,derived classes總是繼承base classes的接口
2.pure virtual函數只具體指定接口繼承
3.簡樸的(非純)impure virtual函數具體指定接口繼承及缺省實現繼承
4.non-virtual函數具體指定接口繼承以及強制實現繼承。如果成員函數是個non-virtual函數,意味着它並不打算在derived classes中有不同的行爲
三十五、條款35-考慮virtual函數以外的其他選擇
1.籍由Non-Virtual Interface手法實現Template Method模式。這一基本設計,也就是“令客戶通過public non-virtual成員函數間接調用private virtual函數”,稱爲non-virtual interface(NVI)手法。它是所謂Template Method設計模式(與C++ templates並無關聯)的一人獨特表現形式。我把這個non-virtual函數稱爲virtual函數的外覆器(wrapper)
2.籍由Function Pointers實現Strategy模式
3.籍由tr1::function完成Strategy模式。tr1::function對象的行爲就像一般函數指針。這樣的對象可接納“與給定之目標籤名式(target signature)兼容”的所有可調用(callable entities)--普通函數、函數對象、成員函數
4.將機能從成員函數移到class外部函數,帶來的一個缺點是,非成員函數無法訪問class的non-public成員
三十六、條款36-絕不重新定義繼承而來的non-virtual函數
1.non-virtual函數都是靜態綁定。這意思是pB被聲明爲一個pointer-to-Base,通過pB調用的non-virtual函數永遠是Base所定義的版本,即使pB指向一個類型爲“Base派生之class”的對象
2.non-virtual函數爲class建立起一個“不變性凌駕特異性”的性質。所以絕對不要重新定義繼承而來的non-virtual函數
三十七、條款37-絕不重新定義繼承而來的缺省參數值
1.virtual函數系動態綁定(dynamically bound),而缺省參數值卻是靜態綁定(statically bound)
2.對象的所謂靜態類型(static type),就是它在程序中被聲明時所採用的類型。而動態類型則是指“目前所指對象的類型”,也就是說,動態類型可以表現出一個對象將會有什麼行爲。如:
Shape* ps;   // 靜態類型爲Shape*,無動態類型
Shape* pc = new Circle;  // 靜態類型爲Shape*,動態類型爲Circle*
Shape* pr = new Rectangle; // 靜態類型爲Shape*,動態類型爲Rectangle*
動態類型一如其名稱所示,可在程序執行過程中改變(通常是經由賦值動作)
ps = pc;   // ps的動態類型如今是Circle*
ps = pr;   // ps的動態類型如今是Rectangle*
3.絕對不要重新定義一個繼承而來的缺省參數值,因爲缺省參數值都是靜態綁定,而virtual函數--你唯一應該覆寫的東西--卻是靜態綁定
三十八、條款38-通過複合塑模出has-a或“根據某物實現出”
1.複合(composition)是類型之間的一種關係,當某種類型的對象內含它種類型的對象,便是這種關係。在程序員之間複合這個術語有許多同義詞,包括layering(分層),containment(內含),aggregation(聚合)和embedding(內嵌)
2.在應用域(application domain),複合意味has-a(有一個)。在實現域(implementation domain),複合意味is-implemented-in-terms-of(根據某物實現出)
三十九、條款39-明智而審慎地使用private繼承
1.private繼承意味is-implemented-in-terms-of(根據某物實現出)。如果你讓class D以private形式繼承class B,你的用意是採用了class B內已經備妥的某些特性,不是因爲B對象和D對象存在有任何觀念上的關係。private繼承意味只有實現部分被繼承,接口部分應略去。其在軟件“設計”層面上沒有意義,其意義只及於軟件實現層面
2.條款38纔剛指出複合的意義也是這樣。你如何在兩者之間取捨?答案很簡單:儘可能使用複合,必要時才使用private繼承。何時纔是必要?主要是當protected成員和/或virtual函數牽扯進來的時候。當你面對兩個“並不存在is-a關係”的兩個classes,其中一個需要訪問另一個的protected成員,或需要重新定義其一或多個virtual函數,private極有可能成爲正統設計策略
3.和複合(composition)不同,private繼承可以造成EBO(empty base optimization:空白其類最優化)。這對致力於“對象尺寸最小化”的程序庫開發者而言,可能很重要
四十、條款40--明智而審慎地使用多重繼承
1.最先要認請的一件事是,當多重繼承(mutiple inheritance:MI)進入設計景框,程序有可能從一個以上的Base classes繼承相同名稱(如函數、typedef等等)。那會導致較多的岐義(ambiguity)機會
2.多重繼承的意思是繼承一個以上的Base classes,但這些base classes並不常在繼承體系中又有更高級的base classes,因爲那會導致要命的“鑽石型MI”。
class File { ... };
class InputFile:public File { ... };
class OutputFile:public File { ... };
class IOFile:public InputFile,
  public OutputFile
{ ... };
任何時候如果你有一個繼承體系而其中某個base class和某個derived class之間有一條以上的相通路線,你就必須面對這樣一個問題:是否打算讓base class內的成員變量經由每

一條路徑被複制?C++在這場辨論中並沒有傾斜立場:兩個方案它都支持--雖然其缺省做法是執行復制。如果那不是你想要的,你必須令那個帶有此數據的class成爲一個virtual

base class,當然,前提是:你得爲virtual繼承付出代價
class File { ... };
class InputFile:virtual public File { ... };
class OutputFile:virtual public File { ... };
class IOFile:public InputFile,
  public OutputFile
{ ... };
3.非必要不使用virtual bases。如果必須使用,儘可能避免在其中放置數據。這麼一來就不需要擔心這些classes身上的初始化(或賦值)所帶來的詭異事情了
4.多重繼承的確有正當用途。其中一個情節涉及“public繼承某個interface class”和“private繼承某個協助實現的class”的兩相結合

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章