【讀書筆記】Effective C++(04)設計與聲明

f作者:LogM

本文原載於 https://segmentfault.com/u/logm/articles,不允許轉載~

4. 設計與聲明

  • 4.1 條款18:讓接口不易被誤用

    • 簡單來說,就是考慮用戶可能的誤用行爲,在代碼中規避。比如工廠函數返回值爲"智能指針"類型,避免用戶使用一般指針管理資源帶來的資源泄漏風險;比如將 operator* 的返回類型設爲const,避免用戶寫出"a*b=c"這樣的代碼。
  • 4.2 條款19:像設計type一樣設計class

    • 作者的意思是,設計class要考慮很多細節,儘量使設計出來的class像C++內置類型一樣有極大的可用性和魯棒性。
  • 4.3 條款20:寧以 pass-by-reference-to-const 替換 pass-by-value

    • 原因:a. 參數使用引用傳遞,不需要構造新對象,比較快;b. 避免對象切割問題。
    • 對象切割(slicing):當派生類對象以 by-value 方式傳遞參數並被視爲基類對象,基類的拷貝構造函數在構造時會把派生類特有的性質抹除。
    • "引用"在編譯器底層的實現就是指針(指針的本質是int類型的變量),所以在以下場景,"引用"並不一定比pass-by-value快:a. C++內置類型;b. STL的迭代器(底層實現是指針);c. 函數對象(底層實現是指針)。
  • 4.4 條款21:不要讓函數返回值是指向局部變量的引用或指針

    • 原因應該很容易理解:局部變量在函數調用結束後就銷燬了,那麼這個函數返回的引用和指針指向的內存已經無效了。
  • 4.5 條款22:將成員變量聲明爲 private

    • 一致性:成員變量爲 private,則用戶想訪問成員變量必須通過成員函數,所以用戶就不用試着記住是不是要加括號,因爲成員函數都要加括號。
    • 安全性:通過成員函數控制用戶對成員變量的讀寫權限。
    • 封裝性:class的版本發生變化,但提供給用戶的API還是可以保持不變。
  • 4.6 條款23:寧以 non-menber、non-friend 替換 member 函數

    • 使用場景如下代碼所示。作者傾向於non-member、non-friend的理由是:它們無法訪問private的成員變量,在封裝性上更好,編譯時候的依賴程度也低。
    • 作者的說法有一定道理,但我不完全同意作者的觀點:

      • a. member函數可以訪問private的成員變量,並不意味着用戶就可以接觸到private的成員變量,你在寫代碼的時候不讓這個member函數訪問private的成員變量不就可以了?(此時問題變成了:如何確保寫代碼的人不在這個函數中濫用private的成員變量)
      • b. 有些情況,使用non-member、non-friend函數會降低代碼接口的一致性。作者的解決思路是把non-member、non-friend函數放在和類同一個namespace下,我想了想,這麼做一致性還是不如直接寫member函數。
    • class WebBrowser {
      public:
          ...
          void clearCache();
          void clearHistory();
          void clearCookies();
          ...
      }
      
      //假如現在要寫一個函數clearEverything(),作用是同時清理cache、history、cookies。
      
      //使用member函數的情況
      class WebBrowser {
      public:
          ...
          void clearEverything();
          ...
      }
      void WebBrowser::clearEverything() {
          clearCache();
          clearHistory();
          clearCookies();
      }
      
      //使用non-member函數的情況
      void clearBrower(WebBrowser& wb) {
          wb.clearCache();
          wb.clearHistory();
          wb.clearCookies();
      }
  • 4.7 條款24:若所有參數都需要類型轉換,請把這個函數寫成 non-member 函數

    • //第一種情況:乘法函數爲member函數
      class Rational {
      public:
          ...
          Rational(int numerator, int denominator);
          Rational(int num);  //這個構造函數使得該類支持從int到Rational的類型轉換。如果前面加explict則說明不支持隱式類型轉換僅支持顯式轉換,現在沒加,支持隱式轉換
          const Rational operator* (const Rational& rhs) const;
      }
      
      //使用
      Rational lhs(1, 9);
      Rational result;
      result = lhs * 2;   //ok,2不是Rational類型,但可以發生隱式類型轉換
      result = 2 * lhs;   //bad,2不是Rational類型
    • //第二種情況:乘法函數爲non-member函數
      class Rational {
      public:
          ...
          Rational(int numerator, int denominator);
          Rational(int num);  //這個構造函數使得該類支持從int到Rational的類型轉換
      }
      
      const Rational operator* (const Rational& lhs, const Rational& rhs) {
        ...
      }
      
      //使用
      Rational lhs(1, 9);
      Rational result;
      result = lhs * 2;   //ok,2不是Rational類型,但可以發生隱式類型轉換
      result = 2 * lhs;   //ok,2不是Rational類型,但可以發生隱式類型轉換
  • 4.8 條款25:考慮寫出一個不拋異常的swap函數

    • STL庫中std::swap的典型實現如下代碼,這個實現比較平淡。對於某些類,寫一個模板特化的swap執行效率會更高。作者介紹了怎麼自己寫std::swap的特化版本,這邊就不展開了。
    • namespace std {   //平淡的std::swap實現
          template<typename T>
          void swap(T& a, T& b) {
              T temp(a);
              a = b;
              b = temp;
          }
      }
發佈了52 篇原創文章 · 獲贊 19 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章