《Effective C++》讀書筆記第四章——設計與聲明(Designs and Declarations)

條款18:讓接口容易被正確使用,不易被誤用

(Make interfaces easy to use correctly and hard to use incorrectly)

  • 好的接口很容易被正確使用,不容易被誤用。
  • “促進正確使用”的辦法包括接口的一致性,以及與內置類型的行爲兼容
  • “阻止誤用”的辦法包括建立新類型、限制類型上的操作,束縛對象值,以及消除客戶的資源管理責任
  • tr1::shared_ptr支持定製型刪除器(custom deleter)。這可防範DLL問題,可被用來自動解除互斥鎖(mutexes;見條款14)等等。

條款19:設計class猶如設計type

(Treat class design as type design)

  • 新type的對象應該如何被創建和銷燬? eg:構造、析構、內存分配和釋放函數
  • 對象的初始化和對象的賦值該有怎樣的差別? eg:構造函數和賦值操作符
  • 新type的對象如果被passed by value,意味着什麼? eg:拷貝構造函數
  • 什麼是新type的“合法值”?
  • 你的新type需要配合某個繼承圖系嗎? eg:virtual
  • 你的新type需要什麼樣的轉換? eg:explicit
  • 什麼樣的操作符和函數對新type是合理的?
  • 什麼樣的標準函數應該駁回? eg:聲明爲private
  • 誰該取用新type成員? eg:friends、public、private
  • 什麼是新type的“未聲明接口”?
  • 你的新type有多麼一般化? eg:template
  • 你真的需要一個新type嗎?

條款20:寧以pass-by-reference-to-const替換pass-by-value

(Prefer pass-by-reference-to-const to pass-by-value)

  • 儘量以pass-by-reference-to-const替換pass-by-value。前者通常比較高效,並可避免切割問題。
    eg:func(base b)可寫成func(const base& b)
  • 以上規則並不適用於內置類型,以及STL的迭代器和函數對象。對它們而言,pass-by-value往往比較適當。

條款21:必須返回對象時,別妄想返回其reference

(Don’t try to return a reference when you must return an object)

  • 絕不要返回pointer或reference指向一個local stack對象,或返回reference指向一個heap-allocated對象,或返回pointer或reference指向一個local static對象而有可能同時需要多個這樣的對象。 條款4已經爲“在單線程環境中合理返回reference指向一個local static對象”提供了一份設計實例。

條款22:將成員變量聲明爲private

(Declare data members private)

  • 切記將成員變量聲明爲private。這可賦予客戶訪問數據的一致性、可細微劃分訪問控制、允諾約束條件獲得保證,並提供class作者以充分的實現彈性。
  • protected並不比public更具封裝性。

條款23:寧以non-member、non-friend替換member函數

(Prefer non-member non-friend functions to member functions)

  • 寧可拿non-member non-friend函數替換member函數。這樣做可以增加封裝性、包裹彈性和機能擴充性。
    eg:將所有便利函數放在多個頭文件內但隸屬同一個命名空間,意味着客戶可以輕鬆擴展這一組便利函數。他們需要做的就是添加更多non-member non-friend函數到此命名空間內。

條款24:若所有參數皆需類型轉換,請爲此採用non-member函數

(Declare non-member functions when type conversions should apply to all parameters)

  • 如果你需要爲某個函數的所有參數(包括被this指針所指的那個隱喻參數)進行類型轉換,那麼這個函數必須是個non-member。
    eg:
    1、當將有理數相乘的操作符聲明爲成員函數時:
    const Rational operator*(const Rational& rhs) const;
    Rational oneHalf(1, 2);
    Rational res1 = oneHalf * 2; // 正確,等價於 Rational res1 = oneHalf.operator*(2); // 發生隱式類型轉換
    Rational res2 = 2 * oneHalf; // 錯誤, 等價於 Rational res2 = 2.operator*(oneHalf);
    因爲oneHalf是一個內含operator* 函數的class的對象,而整數2沒有相應class,也就沒有operator* 成員函數,編譯器也會嘗試尋找可被這樣調用的non-member operator*(也就是在命名空間內或在global作用域內):
    Rational res2 = operator*(2, oneHalf);
    2、因此,可將該相乘操作符聲明爲非成員函數:
    const Rational operator*(const Rational& lhs, const Rational& rhs);

條款25:考慮寫出一個不拋異常的swap函數

(Consider support for a non-throwing swap)

  • 當std::swap對你的類型效率不高時,提供一個swap成員函數,並確定這個函數不拋出異常。
  • 如果你提供一個member swap,也該提供一個non-member swap用來調用前者。對於classes(而非templates),也請特化std::swap。
  • 調用swap時應針對std::swap使用using聲明式,然後調用swap並且不帶任何“命名空間資格修飾”。
  • 爲“用戶定義類型”進行std templates全特化是好的,但千萬不要嘗試在std內加入某些對std而言全新的東西。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章