類與函數的設計和聲明
看看作者思考問題的角度:
對象如何產生和銷毀: 構造析構、new和delete的重載
對象的初始化和賦值:構造、拷貝構造、賦值重載
新型別轉換相關:合法值的規範檢驗、繼承體系、是否隱式轉換
存取權限:public、protected、private如何設計
Item18 -- 努力讓介面完滿且最小化
1,KISS原則;2,標頭檔的重要性;
Item19 -- 區分member functions, non-member functions和friend functions三者
1,如果要實現虛函數,必須是member function
2,讓operator<<和operator>>成為non-members,如果還需要擷取類的非公用成員變數,聲明為friend。原因,如果是func為member,那麼以後書寫順序應該是obj>>cin,obj<<cout,這樣不符合習慣
3,只有non-member才能在最左參數身上實施型別轉換。如果需要對函數f的最左側參數進行型別轉換,那麼f為non-function,如果還需要擷取類的非公用成員變數,聲明為frind。
舉例,operator *(Class &lhs, Class &rhs)這種聲明,2*obj2的調用,需要對2進行型別轉換(建構函式聲明為explicit可以阻止隱式型別轉換),這樣就必須為non-member
Item20 -- 避免將data members放在公開介面中
Effective中舉了三個原因,說明為什麼不要放在公開介面中
- 一致性,以後對類對象的所有操作,均需要帶(),也就是只能調用函數,不能擷取變數
- 擷取控制性,比如唯讀、可讀可寫、不處理,通過不同的函數實現
- 函數抽象性,提供一個借口,底層如何?上層使用者不用關心
不過在實際編程中,很少人能夠完全做到這點,畢竟需要自己花些時間來寫get和set,暫時我也沒找到自動產生get、set函數的方法,所以魚與熊掌不可兼得,若想獲得好處,就得費力寫get、set了。
Item21 -- 儘可能使用const
1,*號在中間,前定內容後定針
2,傳回值用const修飾,說明傳回值是唯讀,不能修改
3,函數後面用const修飾,說明該函數不能修改任何變數。函數可以據此進行重載,有const的函數被const對象調用,沒有const的函數被非const對象調用
const的真正意義是什嗎?不變性,具體的體現有兩種說法:A, bitwise. B, conceptual,A說法對位進行比較,如果沒有修改則認為是不變的。B從概念層面進行判斷,即使底層有修改,但對上層概念來講是不變的,那就是不變的。但是C++語言只支援A,所以為了應付B,引入mutable修飾詞,用來修飾上層概念不變,但是底層要修改的底層變數。
4,const可以通過const_cast取消常量性
Item22 -- 盡量使用pass-by-reference,少用pass-by-value
- C語言裡面都是傳值
- 傳值成本比較大,會調用對象的拷貝構造,如果類比較複雜,則會建立和析構更多的對象
- 傳引用會避免切割問題。Func(base&) 和Func(base)兩種函式宣告,內部調用f()虛函數,如果傳遞個derived對象,則傳引用會調用derived.f(),而傳值則會切割而調用base.f()
Item23 -- 當你必須回傳object時,不要嘗試傳回reference
用重載乘法舉例
Inline const Rational Operator*( const Rational& lhs, const Rational & rhs)
{
return Rational(lhs.n*rhs.n, lhs.d*rhs.d);
} |
傳回的是value,如果傳回reference的話,內部變數析構之後,引用沒有真正的對象
Item24 -- 在函數重載和參數預設化之間,謹慎選擇
void g(int x=0);
g();
g(10); |
void f(); void f(int x);
f();
f(10); |
兩種方式要謹慎選擇,避免出現模稜兩可的情況
Item25 -- 避免對指標型別和數值型別進行重載
void f(int x);
void f(string *ps);
f(0) |
0的存在會對指標和數值造成模稜兩可,所以要堅決避免針對指標和數值進行重載
Item26 -- 防衛潛伏的ambiguity(模稜兩可)狀態
C++有一個哲學信仰,它相信潛在的模稜兩可狀態不是一種錯誤,但是對程式員來講,將所有問題放到運行後發現就是一種災難。所以程式員應該避免模稜兩可。
類的轉換,一是拷貝構造方式可以隱式轉換,一是operator Class()方式,當需要型別轉換時,就會有模稜兩可
- 語言標準轉換,6.02可以轉換成int也可以轉換成char
- 多繼承也是如此
當遇到模稜兩可情況時,程式員應該顯式的說明採用哪種方式。
Item27 -- 如果不想使用編譯器暗自產生的member functions,就應該明白拒絕它
- 使用private修飾防止公開調用
- 不定義防止friend等調用
private:
Array& operator=(const Array &rhs);(注意這裡;表示不定義) |
Item28 -- 嘗試切割global namespace
namespace name1{
}
using namespace name1;
最好每個人都以自己姓名為name,進行分割,這樣可以類似Java中的包的概念