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庫中
的典型實作如下代碼,這個實作比較平淡。對于某些類,寫一個模闆特化的swap執行效率會更高。作者介紹了怎麼自己寫std::swap
的特化版本,這邊就不展開了。std::swap
-
namespace std { //平淡的std::swap實作 template<typename T> void swap(T& a, T& b) { T temp(a); a = b; b = temp; } }
- STL庫中