13.以對象管理資源
- 資源包括:內存、文件描述符、互斥鎖、圖形界面中的字型和筆刷、數據庫連接、網絡sockets 當不再使用資源時,一定要把它歸還給系統
以對象管理資源(資源取得時機便是初始化時機RAII :Resource Acquisition Is Initialization):把資源放進對象內,便可依賴c++的析構函數自動調用機制確保資源被釋放
-
- 獲得資源後理解放進管理對象中
- 管理對象運用析構函數確保資源被釋放
- auto_ptr被銷燬時會自動刪除它所指之物,所以一定要注意別讓多個auto_ptr指針指向同一對象(否則對象會被刪除一次以上,行爲未定義)
auto_ptr指針:若通過copy構造函數或者copy assignment操作符複製它,它會變成null,而複製所得的指針將取得資源唯一擁有權,STL容器不能用auto_ptr
- shared_ptr是一個RSCPs(引用計數型智慧指針,無法打破環狀引用,例如兩個沒使用的對象彼此互指,好像對象還處在被使用狀態),shared_ptr的copy行爲比較直觀
14.在資源管理類中小心copying行爲
複製RAII對象必須一併複製它所管理的資源,所以資源的copying行爲決定RAII對象的copying
普遍而常見的 RAII class copying行爲是:抑制copying、施行引用計數法
15.在資源管理類中提供對原始資源的訪問
- APIs往往要求訪問原始資源,所以沒一個RAII class應該提供一個“取得其所管理之資源”的辦法
- 對原始資源的訪問可能經由顯式轉換或隱式轉換。一般而言顯示轉換比較安全
16.成對使用new 和delete 時要採取相同形式
- 使用new:內存被分配出來 –> 針對此內存會有一個(或更多)構造函數被調用
使用delete:針對此內存一個(或更多)析構函數被調用->釋放內存
- delete xxx; // 認爲指針指向單一對象
delete[] xxx;//認爲指針指向一個數組
17.以獨立語句將newed對象置入智能指針
- 以獨立語句將newed對象存入智能指針內(先創建對象,將它置入一個智能指針內,然後再把智能指針傳給函數調用。)。如果不這樣做,一旦異常被拋出(比如在new對象和執行shared_ptr構造函數之間的某一步異常),有可能導致難以察覺的資源泄露。
- shared_ptr構造函數需要一個原始指針,該構造函數是個explicit構造函數。
18.讓接口容易被使用,不易被誤用
- 促進正確使用:接口的一致性,與內置類型的行爲兼容
- 阻止誤用:建立新類型,限制類型上的操作,束縛對象值,消除客戶的資源管理責任
- shared_ptr支持定製型刪除器,可防範DLL問題(cross-DLL problem:對象在一個DLL中被創建,在另一個DLL中被銷燬),可被用來自動解除互斥鎖等
19.設計class猶如設計type
考慮以下問題:
對象創建、銷燬、初始化、賦值(考慮初始化和賦值的差別)、值傳遞(新type的對象如果以值傳遞,會發生什麼)、合法值、繼承關係(是否需要配合某個繼承圖系)、轉換(新type與其他type之間是否需要轉換及需要什麼轉換)、一般化、操作符、函數(需要暴露及需要private的)
20. 寧以 pass-by-reference-to-const(高效) 替換 pass-by-value(有拷貝操作)
(前者通常更高效、避免切割問題(slicing problem),但不適用於內置類型、STL迭代器、函數對象)
- 切割問題:當一個derived class對象以by value方式傳遞並被視爲一個base class對象,base class的copy構造函數會被調用,而“造成此對象的行爲像個derived class對象”的那些特化性質全被切割掉了,僅僅留下一個base class對象
- pass-by-reference-to-const不適用於STL及內置類型
21.必須返回對象時,不返回其reference
- 棧對象:函數內的對象(local對象)在函數退出前被銷燬了,如果返回某個local對象的reference會引發未定義行爲。
絕不要返回pointer或reference指向一個local stack對象,或返回reference指向一個heap-allocated對象,貨返回pointer或reference指向一個local static對象而有可能同時需要多個這樣的對象
22.將成員變量聲明爲private
- 將成員變量聲明爲private,這可賦予客戶訪問數據的一致性,可細微劃分訪問控制、允許約束條件獲得保證,並提供class作者以充分的實現彈性
- protected並不比public更具封裝性
23.寧以non-member、non-friend替換member函數
- namespace和class不同,前者可以跨越多個源碼文件而後者不可以
- 以non-member、non-friend替換member函數,可以增加封裝性、包裹彈性和機能擴充性
24.若所有參數皆需類型轉換,請爲此採用non-member函數
如果需要爲某個函數的所有參數(包括this指針所指的那個隱喻參數)進行類型轉換,那麼這個函數必須是non-member(否則可能會因爲找不到參數匹配的函數而調用失敗)
25.考慮寫出一個不拋出異常的swap函數
- 當std::swap對你的類型效率不高時,提供一個swap函數,並確定和這個函數不拋出異常。
- 如果你提供一個member swap,也該提供一個non-swap用來調用前者。對於class(而非templates),也要特化swap。
- 調用swap時應針對std::swap使用using聲明式,然後調用swap並且不帶任何“命名空間資格修飾”。
- 爲“用戶特定類型”進行std templates全特化是好的,但千萬不要嘗試在std內加入某些對std而言全新的東西。