《Effective C++》讀書筆記第五章——實現(Implementations)

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

(Postpone variable definitions as long as possible)

  • 儘可能延後變量定義式的出現。這樣做可增加程序的清晰度並改善程序效率。
    eg:你不只應該延後變量的定義,直到非得使用該變量的前一刻爲止,甚至應該嘗試延後這份定義直到能夠給它初值實參爲止。 如果這樣,不僅能夠避免構造(和析構)非必要對象,還可以避免無意義的默認構造行爲。

條款27:儘量少做轉型動作

(Minimize casting)

  • ==如果可以,儘量避免轉型,特別是在注重效率的代碼中避免dynamic_casts。==如果有個設計需要轉型動作,試着發展無需轉型的替代設計。
    eg:
    在派生類中調用基類方法:
    // 錯誤,調用的是“當前對象之基類成分”的副本,如果其修改了對象內容,實際只修改了副本
    static_cast< Window >(*this).onResize();
    // 正確
    Window::onResize();
  • 如果轉型是必要的,試着將它隱藏於某個函數背後。 客戶隨後可以調用該函數,而不需將轉型放進他們自己的代碼內。
  • 寧可使用C++ style(新式)轉型,不要使用舊式轉型。 前者很容易辨識出來,而且也比較有着分門別類的職掌。
    eg:
    1、舊式轉型:
    (T)expression // C風格,將expression轉型爲T
    T(expression) // 函數風格,將expression轉型爲T
    2、新式轉型:
    const_cast< T >(expression) // 常量性轉除
    dynamic_cast< T >(expression) // 安全向下轉型
    reinterpret_cast< T >(expression) // 意圖執行低級轉型,實際動作(及結果)可能取決於編譯器
    static_cast< T >(expression) // 強迫隱式轉換

條款28:避免返回handles指向對象內部成分

(Avoid returning “handles” to object internals)

  • 避免返回handles(包括references、指針、迭代器)指向對象內部。遵守這個條款可增加封裝性,幫助const成員函數的行爲像個const,並將發生“虛吊號碼牌”(dangling handles)的可能性降至最低。

條款29:爲“異常安全”而努力是值得的

(Strive for exception-safe code)

四十年前,滿載goto的代碼被視爲一種美好實踐,而今我們卻致力寫出結構化控制流(structured control flows)。二十年前,全局數據(globally accessible data)被視爲一種美好實踐,而今我們卻致力於數據的封裝。十年前,撰寫“未將異常考慮在內”的函數被視爲一種美好實踐,而今我們致力於寫出“異常安全碼”。時間不斷前進。我們與時俱進!

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

條款30:透徹瞭解inlining的裏裏外外

(Understand the ins and outs of inlining)

  • 將大多數inlining限制在小型、被頻繁調用的函數身上。這可使日後的調試過程和二進制升級(binary upgradability)更容易,也可使潛在的代碼膨脹問題最小化,使程序的速度提升機會最大化。
    eg:
    1、inline函數背後的整體觀念是,將“對此函數的每一個調用”都以函數本體替換之。這樣做可能增加目標碼(object code)大小。在一臺內存有限的機器上,過度熱衷inlining會造成程序體積太大(對可用空間而言)。即使擁有虛內存,inline造成的代碼膨脹亦會導致額外的換頁行爲(paging),降低指令高速緩存裝置的擊中率(instruction cache hit rate),以及伴隨這些而來的效率損失。
    2、inlining在大多數c++程序中是編譯器行爲。
    3、一個表面上看似inline的函數是否真是inline,取決於你的建置環境,主要取決於編譯器。幸運的是大多數編譯器提供了一個診斷級別:如果它們無法將你要求的函數inline化,會給你一個警告信息。
    4、
    inline void f() {…} //假設編譯器有意願inline“對f的調用”
    void (*pf)() = f; //pf指向f

    f(); // 這個調用將被inlined,因爲它是一個正常調用。
    pf(); // 這個調用或許不被Inlined,因爲它通過函數指針達成。
  • 不要只因爲function templates出現在頭文件,就將他們聲明爲inline。

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

(Minimize compilation dependencies between files)

  • 支持“編譯依存性最小化”的一般構想是:相依於聲明式,不要相依於定義式。基於此構想的兩個手段是Handle classes和Interface classes。
  • 程序庫頭文件應該以“完全且僅有聲明式”(full and declaration-only forms)的形式存在。這種做法不論是否涉及templates都適用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章