條款18:讓介面更容易被正確使用,不易被誤用
設計介面的原則:正確性、高效性、封裝性、維護性、延展性以及協議的一致性;
設計原則:1)匯入新型別來預防很多客戶端的錯誤,多使用系統型別(type system(劃分為多個explicit函式),如果某個變數的可選情況有限,可以通過static函式來替代物件,表示某個特定的物件,並將物件的建構函式定義成private來防止新的物件生成,具體**如下:
class month//以函式替代物件,來代替某個特定的月份
private:
explicit month(int m);
date(month::jan( ));//呼叫
2)限制型別內,什麼事情可以做,什麼事情不可以做,通常加上限制const;
3)讓你的types的行為和內建types行為一致,提供行為一致的介面;
4)使用智慧型指標來管理系統資源,可以避免記憶體洩露(注意智慧型指標的管理方式和管理條件(單個物件),並用自定義的deleter(資源釋放函式)繫結在智慧型指標上,deteler(可預設)),**如下:
std::tr1::shared_ptrpinv(static_cast(0),getridofinvestment);
//shared_ptr第乙個引數必須要求是乙個指標,乙個空指標shared_ptr
//getridofinvestment為刪除器
這樣的初始化不是最優的:先把pinv初始化為nullptr後對其進行賦值操作(相當於初始化過程是多餘的,如果知道初始指標的話,直接用初始指標來初始化pinv才是最優的);
使用智慧型指標的另外乙個好的特性,它會自動使用它的各乙個指標專屬的刪除器,因而消除乙個潛在的客戶錯誤(cross-dll problem),即;物件在動態連線程式庫(dll)中被new建立,卻在另外乙個dll內被delete,這一類稱為「跨dll之new/delete成對運用」會導致執行期錯誤,但是智慧型指標沒有這個問題,它預設的刪除器來自於智慧型指標誕生的那個delete;
boost的shared_ptr指標是原始指標(raw pointer)的兩倍大,以動態分配記憶體作為簿記用途和「刪除器之專屬資料」,以vrtual形式呼叫刪除器,並以多執行緒程式修改引用次數時承受執行緒同步化的額外開銷,但這些額外的執行成本並不顯著,但是在降低客戶錯誤的方面卻起到了很好的效果;
條款19:設計class猶如設計type
在設計乙個新的class的時候,就像設計乙個新的type,在設計乙個高效的class的時候,應該考慮的問題有:
1)物件如何被建立和銷毀;
2)物件初始化和賦值之間有什麼差別;
3)新物件在傳值和傳引用方面的考慮;
4)對於成員變數引申的成員函式需要進行的錯誤檢查工作及函式丟擲異常及異常處理;
5)考慮型別的繼承和被繼承情況;
6)型別需要做什麼樣的轉換,編寫對應的型別轉換函式;
7)什麼函式應該是成員函式,什麼不是;
8)什麼函式應該定義為private;9)成員變數的屬性;
9)保證效率、異常安全性以及資源利用(多工鎖定和動態記憶體);
10)一般化你的型別,考慮定義乙個模板類;
11)是否需要定義類,可以在別人類的基礎上新增乙個或者多個non-member函式或者template;
條款20:寧以pass-by-reference-to-const替換pass-by-value
預設情況下c++以by-value方式傳遞物件到函式,函式引數都是以實際實參的副本為初值,而呼叫端所獲得的也是函式返回值的乙個附件,這些副本由物件的copy建構函式產生,使得pass-by-value稱為乙個函式操作,如果有很多成員變數的時候;
如果採用傳遞引用的方式,沒有任何的建構函式或者析構函式呼叫,因為沒有任何的物件被建立;除此之外,以傳引用的方式傳遞引數可以避免slicing(物件切割)的問題,即當乙個對derived class物件以by value的方式傳遞並視為乙個base class物件,base class的拷貝建構函式被呼叫,但是derived class區別於base class的部分會被切割掉,而只留下乙個base class;
避免是slicing(切割)問題的方法,就是採用by-reference-to-const的方式傳遞,原因是因為reference往往是以指標的形式實現,pass by reference通常意味著傳遞的是指標;
一般而言,對於內建型別、stl的迭代器和函式物件,採用傳值比較方便,其他最好使用傳引用;
條款21:必須返回物件時,別妄想返回其reference
對於reference只是乙個名稱,代表某個既有物件。任何時候看到乙個reference宣告式,應該去了解它的另外乙個名稱是什麼?
分析**如下:
//返回引用指向棧上的變數
錯誤方法1.const rational& operator*(const rational&lhs,const rational &rhs){
rational result(lhs.n*rhs.n,rhs.d*rhs.d);//試圖返回乙個棧空間變數為引用
return result; //物件在函式呼叫結束後被銷毀,引用指向乙個錯誤的物件;
//返回引用指向堆上的變數
錯誤方法2.const rational& operator*(const rational&lhs,const rational &rhs){
rational *result=new rational(lhs.n*rhs.n,rhs.d*rhs.d);//如何對這個new記憶體進行管理
return *result; //沒辦法取得reference背後的指標,如何a*b*c時更容易出錯;
//返回引用指向乙個函式內部的static rational物件
錯誤方法3.const rational& operator*(const rational&lhs,const rational &rhs){
static rational result; //staitc物件會導致多執行緒安全性
result=rational temp(lhs.n*rhs.n,rhs.d*rhs.d);//static公用問題會帶來錯誤的影響;
return result;
rational a,b,c,d; if(a*b==c*d)//if裡面的資料一直為真,不管其他的怎麼取值,因為static原因和返回是引用的原因
正確**:inline const rational operator*(const rational&lhs,const rational &rhs){
rational result(lhs.n*rhs.n,rhs.d*rhs.d);//雖然會有拷貝構造和析構的成本,但是對於安全性考慮卻是乙個很好的做法;
結論:不要返回pointer或者reference指向乙個local stack物件(見錯誤方法1),或返回乙個reference指向乙個heap-allocated物件9見錯誤方法2),或返回pointer或reference指向乙個local static物件而有可能同時需要多個這樣的物件(見錯誤方法3);
條款22:將成員變數宣告為private
如果成員變數不是public,客戶能夠訪問物件的方式就是通過成員變數函式,如果public介面內的每樣東西都是函式,那麼可以避免客戶在呼叫class成員時考慮是否需要使用小括號;如果你通過函式訪問成員變數,那麼你以後你改變成員變數時,class客戶卻完全不知道class 內部發生了變化;
將成員變數隱藏在函式介面的背後沒可以為「所有可能的實現」提供彈性;你對客戶隱藏成員變數(封裝性),你可以確定class的約束條件得到維護,因為只有成員函式可以影響它們;
某個東西的封裝性與「當其內容改變時可能造成的**破壞量」成反比,改變就是從class中移除它;
一旦你宣告成員變數為public或者protected時,就很難改變這個成員變數涉及的一切,有太多的**需要重寫、重新測試、重新編寫、重新編譯;
結論:將成員變數宣告為private,這可以賦予客戶訪問資料的一致性、可細微劃分訪問控制、允諾約束條件獲得保證,並為class提供充分的實現彈性;
第六天學習
變數的作用域 區域性變數 在函式內部定義的變數,這個變數只能在函式內部使用,在全域性當中不能使用。使用就報錯了。全域性變數 在函式外部定義的變數,這個變數可以在全域性使用。但是我們一般不推薦使用全域性變數 因為可能會意外的修改掉變數的值。迫不得已不要用全域性變數 衝突處理原則 就近原則。而不是從上到...
學習第六天
還有九天,我就要去參加北大3日遊了。滑稽 像我這樣的蒟蒻去那也就是旅遊模式吧!無所謂了,隨便去考考,說不定有優秀營員呢?滑稽 不可能,絕對不可能 相信自己好了。這麼多天,好像把面試給忘了,雖然有人說面試是瞎 但是還是要準備準備的 明天看吧!還有什麼知識點沒看嗎?我們來看看st表和hash表吧!先看看...
學習android第六天
activity簡介 當在乙個activity中按下返回時,android os會自動銷毀activity物件 當在乙個activity中啟動另乙個activity時,當前activity不會被銷毀,而是被壓入activity棧中 activity的建立 1 自定義類,繼承activity 2 複寫...