Part1一、讓自己習慣C++
條款01:視C++為一個語言聯邦
條款02:盡量以const,enum,inline替換 #define
#define ASPECT_RATIO 1.653
1. 如果你的編譯器支持在類內對const static 整數類型聲明時獲初值,則使用
2. 如果不支持,則在類內定義,在對應的實現文件中賦值
條款 03:盡可能使用const
1. 他們使class接口比較容易理解
2. 他們使得可以操作const對象
條款 04:確定對象被使用前已先被初始化
class FileSystem{...};
FileSystem& tfs(){
static FileSystem fs;
return fs;
}
Part2二、構造/析構/賦值運算
條款05:了解C++默默編寫并調用了哪些函數
1. 類中含有**引用**的成員變量
2. 類中含有**const**的成員變量
3. 類的**基類**中的拷貝賦值運算符是**私有**成員函數
條款06:若不想使用編譯器自動生成的函數,就應該明確拒絕
條款07:為多態基類聲明virtual析構函數
1. 用來作為帶有多態性質的基類的類
2. 一個類中帶有任何virtual函數
條款08:別讓異常逃離析構函數
條款09:絕不再構造和析構函數中調用virtual函數
解決辦法之一:
class Transaction{
publci:
explicit Transaction(const std::string& logInfo);
void logTransaction(const std::string& logIngo) const;//把它變成這樣的non-virtual函數
...
};
Transaction::Transaction(const std::string& logInfo){
...
logTransaction(logInfo);//這樣調用
}
class BuyTransaction: public Transaction{
BuyTransaction( parameters ):Transaction(createLogString( parameters )){...}//將log信息傳給基類的構造函數
private:
static std::string createLogString( parameters );//注意此函數為static函數
}
條款10:令operator= 返回一個reference to *this
條款11:在operator= 中處理“自我賦值”
Widget::operator=(const Widget& rhs){
delete pb; //把自己釋放了
pb = new Bitmap(*rhs.pb);//這就不安全了
return *this;
}
先驗證是不是相同的,是不是自我賦值
Widget::operator=(const Widget& rhs){
if(this == &rhs) return *this;//驗證是不是相同
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
在復制pb所指的東西之前別刪除pb
Widget::operator=(const Widget& rhs){
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);//讓pb指向*pb的一個副本
delete pOrig; //刪除原先的pb
return *this;
}
使用交換數據的函數
class Widget{
...
void swap(Widget& rhs);//交換*this和rhs的數據
...
};
Widget::operator=(const Widget& rhs){
Widget temp(rhs);//創建一個rhs副本
swap(temp);//交換*this和上面的副本
return *this;
}
條款12:復制對象時勿忘其每一個成分
Part3三、資源管理
條款13:以對象管理資源
條款14:在資源管理類中心copying行為
1. 當我們處理不能同步擁有的資源的時候,可以才用**禁止復制**,如把copying操作聲明為private
2. 當我們希望共同擁有資源的時候,可以采用**引用計數法**,例如使用shared_ptr
3. 當我們需要拷貝的時候,可以采用**深拷貝**
4. 或者某些時候我們可以采用**轉移**底部資源擁有權的方式
條款15:在資源管理類中提供對原始資源的訪問
class Font{
...
FontHandle get() const {return f;} //顯示轉換
...
operator FontHandle() const {return f;} //隱式轉換函數
....
private:
FontHandle f; //管理的原始資源
}
條款16:成對使用new和delete時要采用相同形式
條款17:以獨立語句將newed對象置入智能指針
諸如這樣的語句processWidget (std::tr1::shared_ptr
1. 在先執行new Widget`語句和調用std::tr1::shared_ptr構造函數之間
2. 不能確定priority函數的執行順序,可能在最前面,也可能在他們的中間
Part4四、設計與聲明
條款18:讓接口容易被正確使用,不易被誤用
條款19:設計class猶如設計type
1. 合理的構建class的構造函數、析構函數和內存分配函數以及釋放函數
2. 不能把初始化和賦值搞混了
3. 如果你的類需要被用來以值傳遞,復制構造函數應該設計一個通過值傳遞的版本
4. 你應該給你的成員變量加約束條件,保證他們是合法值,所以你的成員函數必須擔負起錯誤檢查工作
5. 如果你是派生類,那么你應該遵守基類的一些規范,如析構函數是否為virtural
6. 你是否允許你的class有轉換函數,,是否允許隱式轉換。如果你只允許explicit構造函數存在,就得寫出專門負責執行轉換的函數
7. 想清楚你的類應該有哪些函數和成員
8. 哪些應該設計為私有
9. 哪個應該是你的friend,以及將他們嵌套與另一個是否合理
10. 對效率,異常安全性以及資源運用提供了哪些保證
11. 如果你定義的不是一個新type,而是定義整個type家族,那么你應該定義一個類模板
12. 如果只是定義新的字類以便為已有的類添加機制,說不定單純定義一個或多個non-member函數或模板更好
條款20:寧以pass-by-reference-to-const替換pass-by-value
條款21:必須返回對象時,別妄想返回其reference
條款22:將成員變量聲明為private
1. 為了保證一致性
2. 可以細微的劃分訪問和控制以及約束
3. 內部更改后不影響使用
protected并不比public更具封裝性
條款23:寧以non-member、non-friend、替換member函數
條款24:若所有參數皆需要類型轉換,請為此采用non-member函數
條款25:考慮寫出一個不拋出異常的swap函數
Part5五、實現
條款26:盡可能延后變量定義式的出現時間
條款27:盡量少做轉型動作
const_cast 通常被用來將對象的常量性轉除。它也是唯一有此能力的轉型操作符 dynamic_cast 主要用來執行“安全向下轉型” ,也就是用來決定對某對象是否歸屬繼承體系中的某個類型。它是唯一無法由舊式語法執行的動作,也是唯一可能耗費重大運行成本的轉型動作
reinterpret_cast 意圖執行低級轉型,實際動作(及結果)可能取決于編譯器,這也就表示它不可移植。例如將一個pointer to int轉型為一個int。這一類轉型在低級代碼以外很少見。
static_cast 用來強迫隱式轉換,例如將non-const對象轉換為const對象,或將int轉為double等等,它也可以用來執行上述多種轉換的反向轉換,例如將void* 指針轉為 type 指針,將pointer-to-base 轉為 pointer-ro-derived 。但它無法將 const 轉為 non-const ——這個只有const_cast才能辦到
舊式轉型使用的時機是,當要調用一個explicit構造函數對一個對象傳遞給一個函數時,其他盡量用新式轉型
請記住以下:
1. 如果可以的話,避免dynamic_cast轉型,如果實在需要,則可以試著用別的無轉型方案代替
2. 如果轉型是必要的,那么應該把他隱藏于某個函數背后,客戶隨后可以調用該函數,而不是需要將轉型放進自己的代碼里
3. 寧可要新型轉型,也不要使用舊式轉型
條款28:避免返回handles指向對象內部成分
條款29:為“異常安全”而努力是值得的
1. 首先以對象管理資源可以阻止資源泄漏
2. 在你能實現的情況下,盡量滿足以上的最高等級
條款30:透徹了解inlining 的里里外外
1. 隱喻的inline申請,即把定義寫在class內部
2. 明確聲明,即在定義式前加上關鍵字inline
條款31:將文件間的編譯依存關系降至最低
1. 頭文件和實現相分離,頭文件完全且僅有聲明式
2. 使用創建接口類
Part6六、繼承與面向對象設計
條款32:確定你的public繼承塑模出is-a關系
條款33:避免遮掩繼承而來的名稱
條款34:區分接口繼承和接口實現
條款35:考慮virtual函數以外的其他選擇
條款36:絕不重新定義繼承而來的non-virtual函數
條款37:絕不重新定義繼承而來的缺省參數值
條款38:通過復合塑模has-a或“根據某物實現出”
條款39:明智而審慎地使用private繼承
條款40:明智而審慎地使用多重繼承
Part7七、模板與泛型編程
條款41:了解隱式接口和編譯期多態
條款42:了解typename的雙重意義
templete<typename T>
class Derived:public Base<T>::Nested{ //基類列表中不可以加“typename”
public:
explicit Derived(int x): Base<T>::Nested(x){//mem.init.list中不允許“typename”
typename Base<T>::Nested temp; //這個是嵌套從屬類型名稱
... //作為一個基類修飾符需要加上typename
}
}
條款43:學習處理模板化基類內的名稱
1. 在基類函數調用之前加上 this->
2. 使用 using 聲明式 ,告訴編譯器,請它假設這個函數存在
3. 指出這個函數在基類中,使用基類::函數的形式寫出來(不推薦這個,因為如果是virtual函數,則 會影響動態綁定)
但是當有模板全特化的時候,確實使用的沒有這個函數,那么依然會報錯
條款44:將與參數無關的代碼抽離出來
條款45:運用成員函數模板接受所有兼容類型
條款46:需要類型轉換時請為模板定義非成員函數
Part8八、定制new和delete
條款49:了解new—handler的行為
1. 讓更多內存可被使用。此策略的一個做法是,程序一開始就分配一大塊內存,而后當其第一次被調用,將它釋還給程序使用
2. 安裝另一個new—handler。可以設置讓其調用另一個new—handler來替換自己,用來做不同的事情,其做法是調用set_new_handler
3. 卸載new—handler,也就是將null指針傳給set_new_handler,這樣new在分配不成功時拋出異常
4. 拋出bad_alloc的異常。
5. 不返回,調用abort或exit
6. C++并部支持類的專屬new—handler,但其實也不需要。你可以令每個類提供自己的set_new_handler和operator new即可
set_new_handler允許客戶指定一個函數,在內存分配無法獲得滿足時調用。
Nothrow new是一個頗為局限的工具,因為它只適用于內存分配:后繼的構造函數調用還是可能拋出異常
條款50:了解new和delete的合理替換時機
條款51:編寫new和delete時需固守常規
條款52:寫了placement new也要寫placement delete
Part9九、雜項討論
條款53:不要輕忽編譯器的警告
條款54:讓自己熟悉包括TR1在內的標準程序庫
條款55:讓自己熟悉Boost
往期推薦