Effective C++ 筆記 part 5、6

26.儘可能延後變量定義式的出現時間

  1. 儘可能延後變量的定義知道能夠給它賦初值實參爲止,這樣做可以增加程序的清晰度並改善程序效率。否則會付出不必要的構造和析構成本。
  2. 當遇到循環時:若一個class的一個賦值成本低於一組構造和析構成本,在循環外聲明變量,尤其是循環次數很多的時候。否則應在循環裏聲明變量。

27.儘量少做轉型動作

  1. const_cast通常被用來將對象的常量性移除

dynamic_cast主要用來執行“安全向下轉型”,可能耗費巨大運行成本

reinterpret_cast意圖執行低級轉型,實際動作(及結果)取決於編譯器(即表明其不可移植)

static_cast用來強迫因式轉換,例如將non-const轉化爲const對象,或講int轉化爲double等,但無法將const轉化爲non-const,將point-to-base轉爲point-to-derived,將void*轉化爲typed指針

  1. 單一對象(例如一個derived對象)可能擁有一個以上的地址(例如“以base*指向它時的地址”和“以derived*指向它的地址”)
  2. 在子類虛函數中調用父類的同名虛函數(父類名::函數名(參數列表))

28.避免返回handles指向對象內部成分(成員變量和不被公開使用的成員函數)

  1. 成員變量的封裝性最多隻等於“返回其reference”的函數的返回級別(成員變量是private,但是函數返回其reference,則該變量相當於一個public的變量)
  2. 如果const成員函數傳出一個reference,後者所指數據與對象自身有關聯,而它又被存儲於對象之外,那麼這個函數的調用者可以修改那筆數據
  3. 不被公開使用的成員函數也是對象“內部”的一部分,因此也應留心不要返回它們的handles.(不應該令成員函數返回一個指針指向“訪問級別較低”的成員函數,否則後者的實際訪問級別就會提前如前者,返回“代表對象內部”的handles,可能導致空懸或虛吊,臨時變量被銷燬導致)

29.爲“異常安全性”而努力是值得的

  1. 異常安全函數即使發生異常也不會邪路資源或允許任何數據結構敗壞。這樣的函數分爲三種可能的保證:基本型、強烈性、不拋任何異常型。
  2. “強烈保證”往往能夠以copy-andswap實現出來,但“強烈保證”並非對所有函數都可實現或具備現實意義
  3. 函數提供的“異常安全保證”通常最高只等於其所調用的各個函數的“異常安全保證”中的最弱者

30.透徹瞭解inlining的裏裏外外

  1. inlining 在大多數 C++ 程序中是編譯期的行爲;
  2. inline 函數是否真正 inline,取決於編譯器;大部分編譯器拒絕太過複雜(如帶有循環或遞歸)的函數 inlining,而所有對 virtual 函數的調用(除非是最平淡無奇的)也都會使 inlining 落空;
  3. inline 造成的代碼膨脹可能帶來效率損失;inline 函數無法隨着程序庫的升級而升級

31.將文件間的編譯依存關係降至最低

  1. 如果使用 object references 或 object pointers 可以完成任務,就不要使用 objects;
  2. 如果能夠,儘量以 class 聲明式替換 class 定義式;爲聲明式和定義式提供不同的頭文件)

string不是class,是一個typedef (定義爲basic_string<char>)

Handle classes和Interface class解除了接口和實現之間的耦合關係,從而降低文件之間的編譯依存性。相依於聲明式,不要想依於定義式。

程序庫頭文件應該以“完全且僅有聲明式”的形式存在,這種做法無論是否涉及templates都適用。

32.確定你的public繼承塑模出is-a關係

  1. “public繼承”意味is-a,適用於base class身上的每一件事也一定適用於derived class上,因爲每一個derived class對象也都是一個base class對象

33.避免遮掩繼承而來的名稱

內層作用域的名稱會遮掩外層作用域的名稱,無論名稱類型是否相同。

derived class內的名稱會遮掩base class內的名稱,在public繼承下不希望如此。

爲使用被遮掩了的名稱(在derived class中調用父類的同名函數),可以使用using聲明式或轉交函數。

34.區分接口繼承和實現繼承

  1. 接口繼承和實現繼承不同,在public繼承之下,derived class總是繼承base class的接口
  2. pure virtual函數只具體指定接口繼承

簡樸的impure virtual函數具體指定接口繼承及缺省實現繼承

non-virtual函數具體指定接口繼承以及強制性實現繼承

35.考慮virtual函數以外的其他繼承

NVI手法(non-virtual interface):TemplateMethod設計模式 的一個獨特表現形式。令客戶通過public non-virtual成員函數間接調用private virtual函數。它以public  non-virtual成員函數包裹較低訪問性的virtual函數

  1. Strategy設計模式

將virtual函數替換爲“函數指針成員變量”,這是Strategy設計模式的一種分解表現形式

以tr1::function成員變量替換virtual函數,因爲允許使用任何可調用物搭配一個兼容於需求的簽名式,這也是Strategy設計模式的某種表現形式

將繼承體系內的virtual函數替換爲另一個繼承體系內的virtual函數。這是Strategy設計模式的傳統實現手法

36.絕不重新定義繼承而來的non-virtual函數

class D:public B{…}D中重定義了B中繼承而來的non-virtual函數mf,會出現以下情況

D x;

B* pb=&x;

P* pd=&x;

pB->mf();//調用B中的mf

pD->mf();//調用D中的mf

假設mf是virtual函數,則pB->mf()、pD->mf()都會調動pD->mf函數

37.絕不重新定義繼承而來的缺省參數值

virtual函數是動態綁定(後期綁定),缺省參數值是靜態綁定(前期綁定)

否則可能會發生“在調用一個定義於derived class內的virtual函數”的同時,卻適用base class爲它所指定的缺省參數值

38.通過複合塑模處has-a或“根據某物實現出”

複合的意義與public繼承完全不同

在應用域,複合意味着has –a,在實現域複合以爲is-implemented-in-terms-of(根據某物實現出)

39.明智而審慎地使用private繼承

  1. 如果classes之間的繼承關係是private,編譯器不會自動將一個derived class對象轉化爲一個base class對象。
  2. 由private base class繼承而來的所有成員,在derived class中都會變成private屬性,即使它們在base class中原本是protected或public屬性。
  3. 儘可能使用複合,必要時纔是用private繼承(當protected成員或virtual函數牽扯進來的時候)

class Empty{}  Empty p; // sizeof(p)=1;凡是獨立對象都必須有非零大小

當兩個並不存在is-a關係的兩個class,其中一個需要訪問另一個的protected成員,或需要重新定義其一或多個virtual函數,private繼承可以考慮。

private繼承可以造成empty base最優化

40.明智而審慎地使用多重繼承

  1. virtual繼承的成本:

使用virtual繼承的那些classes所產生的對象往往比使用non-virtual繼承的對象體積大,訪問virtual base classes的成員變量時,也比訪問non-virtual base classes的成員變量速度慢,支持“virtual base classes初始化”的規則比起non-virtual bases的情況遠爲複雜且不直觀

非必要不使用virtual bases

如果必須使用virtual base classes儘可能避免在其中放置數據。

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