1. 程式人生 > 其它 >[筆記]《Effective C++》第五章 Implementations

[筆記]《Effective C++》第五章 Implementations

條款26:Postpone variabledefinitions as long as possible.

儘可能延後變數定義式的出現。這樣做可增加程式的清晰度並改善程式效率

  • 你不只應該延後變數的定義,直到非得使用該變數的前一刻為止,甚至應該嘗試延後這份定義直到能夠給它初值實參為止

條款27:Minimize casting.

  1. 如果可以,儘量避免轉型,特別是在注重效率的程式碼中避免dynamic_casts。如果有個設計需要轉型動作,試著發展無需轉型的替代設計。
  2. 如果轉型是必要的,試著將它隱藏於某個函式背後。客戶隨後可以呼叫該函式,而不需將轉型放進他們自己的程式碼內。
  • 寧可使用C++-style(新式)轉型
    不要使用舊式轉型。前者很容易辨識出來,而且也比較有著分門別類的職掌。

C++提供四種新式轉型:

  • const_cast通常被用來將物件的常量性轉除(cast away the constness)。

  • dynamic_cast主要用來執行“安全向下轉型”(safe downcasting),也就是用來決定某物件是否歸屬繼承體系中的某個型別。dynamic_cast的許多實現版本執行速度相當

  • reinterpret_cast意圖執行低階轉型,實際動作(及結果)可能取決於編譯器,這也就表示它不可移植

  • static_cast用來強迫隱式轉換(implicit conversions),例如將non-const物件轉為const物件(就像條款3所為),或將int轉為double等等。

  • 轉型並不是什麼都不做,任何一個型別轉換(不論是通過轉型操作而進行的顯式轉換,或通過編譯器完成的隱式轉換)往往真的令編譯器編譯出執行期間執行的碼。

我們可以避免使用dynamic_cast,需要dynamic_cast通常是因為你想在一個你認定為derived class物件身上執行derived class操作函式,但你的手上卻只有一個“指向base”的pointerreference,你只能靠它們來處理物件。有兩種一般性的做法避免這個問題:

  • 第一,使用容器並在其中儲存直接指向derived class物件的指標(通常是智慧指標)。
  • 另一種做法可讓你通過base class介面處理“所有可能之各種Window派生類”,那就是在base class內
    提供virtual函式做你想對各個派生類做的事。

條款28:Avoid returning "handles" to object internals.

避免返回handles(包括references指標迭代器)指向物件內部

  • 成員變數的封裝性最多隻等於“返回其reference”的函式的訪問級別。
  • 如果const成員函式傳出一個reference,後者所指資料與物件自身有關聯,而它又被儲存於物件之外,那麼這個函式的呼叫者可以修改那筆資料

條款29:Strive for exception-safe code.

  1. 異常安全函式(Exception-safe functions)即使發生異常也不會洩漏資源或允許任何資料結構敗壞。這樣的函式區分為三種可能的保證:基本型、強烈型、不拋異常型。
  2. “強烈保證”往往能夠以copy-and-swap實現出來,但“強烈保證”並非對所有函式都可實現或具備現實意義。
  3. 函式提供的“異常安全保證”通常最高只等於其所呼叫之各個函式的“異常安全保證”中的最弱者

帶有異常安全性的函式在異常被丟擲時,需要做到:

  • 不洩漏任何資源。
  • 不允許資料敗壞。

三種保證:

  • 基本承諾:如果異常被丟擲,程式內的任何事物仍然保持在有效狀態下。
  • 強烈保證:如果異常被丟擲,程式狀態不改變。
  • 不拋擲(nothrow)保證:承諾絕不丟擲異常,因為它們總是能夠完成它們原先承諾的功能。

copy-and-swap常被用來實現強烈保證,其原則為:

  • 為你打算修改的物件(原件)做出一份副本,然後在那副本身上做一切必要修改。若有任何修改動作丟擲異常,原物件仍保持未改變狀態。待所有改變都成功後,再將修改過的那個副本和原物件在一個不丟擲異常的操作中置換(swap)

條款30:Understand the ins and outs of inlining.

  • inline函式背後的整體觀念是,將“對此函式的每一個呼叫”都以函式本體替換之
  • Inlining在大多數C++程式中是編譯期行為
  • inline只是對編譯器的一個申請,不是強制命令
  • 所有對virtual函式的呼叫(除非是最平淡無奇的)都會使inlining落空。因為virtual意味“等待,直到執行期才確定呼叫哪個函式”,而 inline意味“執行前,先將呼叫動作替換為被呼叫函式的本體”。
  • 建構函式解構函式往往是inlining 的糟糕候選人。
  • 大部分偵錯程式面對inline函式都束手無策。你如何在一個並不存在的函式內設立斷點(breakpoint)

條款31:Minimize compilation dependencies between files.

  1. 支援“編譯依存性最小化”的一般構想是:相依於宣告式,不要相依於定義式
  2. 程式庫標頭檔案應該以“完全且僅有宣告式”(full and declaration-only forms)的形式存在。這種做法不論是否涉及templates都適用。

以“宣告的依存性”替換“定義的依存性”:

  • 如果使用object references或 object pointers 可以完成任務,就不要使用objects。
  • 如果能夠,儘量以class宣告式替換class定義式。
  • 為宣告式和定義式提供不同的標頭檔案。