《Effective C++》筆記:Tips13-Tips17

Tips13:以對象管理資源

一、用對象的特性來管理資源

最常用的資源就是,new出來的內存,即動態分配內存,此外還有數據庫連接,網絡sockets等等。重要的是,一旦使用完這些資源,必須給系統。

所謂,用對象管理資源,就是利用對象銷燬時,自動調用析構函數在析構函數內釋放這些資源,從而在某種程度上達到自動釋放資源的目的


二、RAII對象

RAIIResource Acquisiton Is Inintialization.取得資源時便初始化,即獲得資源的同時,就使用該資源初始化管理對象。


PS:《Effective C++》關於智能指針的建議是關於auto_ptr,tr1::shared_ptr的,而在C++11新標準中,auto_ptr可以由unique_ptr代替, tr1::shared_ptr可以使用標準庫的std::shared_ptr中。


Tips14:在資源管理類中小心copying行爲

一、當RAII(可視爲資源管理類)被複制時,會發生什麼?

本質問題是,當RAII被複制時,其管理的資源該怎麼處理。

1.禁止複製。像是C++中的輸入輸出流,就是禁止複製。通過兩種方法可以達到禁止複製

  • 拷貝函數聲明爲private,且不需要實現

  • C++ 11:在拷貝函數的聲明後加上“=delete


2.對底層使用“引用計數法”。《Effective》內使用的是tr1::shared_ptr,但現在有了更好的選擇std::shared_ptr


3.複製底部資源。這就需要自定義拷貝函數,達到深度拷貝的功能


4.轉移底部資源的擁有權。自始至終,只能有一個對象管理類。《Effective》使用的是auto_ptr,C++ 11新標準中由unique_ptr,能比auto_ptr實現更多特性


Tips15:在資源管理類中提供對原始資源的訪問

一、對原始資源的訪問的兩種方式。

在某些情況,必須直接訪問原始資源。例如 C API 沒有class的概念


1. 顯式。RAII提供一個成員函數,返回原始資源。如shared_ptrunique_ptr,都提供get()函數,返回指向資源的原始指針

2. 隱式。爲RAII提供到原始資源的隱式轉換,即重載operator()

class Font{
public:
       ...
       operator FontHandle() const;     
private:
       FontHandle f; 
};


然而在複製Font對象時,可能就會出現問題

Font f1(getFont());
FontHandle f2 = f1;

在複製的過程中,f1被隱式轉換成FontHandle,然後複製給f2。也許f2會複製一份副本,也許f2直接指向f1的資源。如果是後者,一旦f1銷燬,資源被釋放,f2就成爲類似空懸指針。


Tips16:成對使用newdelete時要採取相同形式

new單一對象,使用delete銷燬

new對象數組,使用delete[] 銷燬對象數組


Tips17:以獨立語句將newed對象置入智能指針。

轉換爲代碼就是

T* tPtr = new T();
shared_ptr<T> p(tPtr);

對於這樣一個函數

void processWidget(shared_ptr<Widget> pw, int priority);
int priority();

現在調用

processWidget(shared_ptr<Widget>(new Widget()), priority());

C++是不能確定以上兩個參數的方法,是按什麼順序調用的。如果出現這樣一種情況,先調用priority(),但調用的過程中出現異常,這就導致智能指針沒用正確初始化。在對processWidget的調用過程中,就出現的資源泄露。

注意這樣段代碼

Widget * tPtr = new Widget ();
processWidget(shared_ptr<Widget>(tPtr), priority());
Widget widget = *tPtr;//tPtrz指向的內存已被釋放,tPtr成爲空懸指針

對於此的建議是,既然用內置指針去初始化智能指針,那就不要使用內置指針

或者,一開始就使用智能指針,內置指針僅用作初始化(臨時對象)    

shared_ptr<Widget> pw(new T());     
processWidget(pw, priority());




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章