最經常使用的資源就是,new出來的內存,即動態分配內存,此外還有數據庫鏈接,網絡sockets等等。重要的是,一旦使用完這些資源,必須給系統。數據庫
所謂,用對象管理資源,就是利用對象銷燬時,自動調用析構函數,在析構函數內釋放這些資源,從而在某種程度上達到自動釋放資源的目的數組
RAII:Resource Acquisiton Is Inintialization.取得資源時便初始化,即得到資源的同時,就使用該資源初始化管理對象。網絡
PS:《Effective C++》關於智能指針的建議是關於auto_ptr,和tr1::shared_ptr的,而在C++11新標準中,auto_ptr能夠由unique_ptr代替, tr1::shared_ptr可使用標準庫的std::shared_ptr中。socket
本質問題是,當RAII被複制時,其管理的資源該怎麼處理。ide
1.禁止複製。像是C++中的輸入輸出流,就是禁止複製。經過兩種方法能夠達到禁止複製函數
拷貝函數聲明爲private,且不須要實現ui
C++ 11:在拷貝函數的聲明後加上「=delete」spa
2.對底層使用「引用計數法」。《Effective》內使用的是tr1::shared_ptr,但如今有了更好的選擇std::shared_ptr指針
3.複製底部資源。這就須要自定義拷貝函數,達到深度拷貝的功能orm
4.轉移底部資源的擁有權。自始至終,只能有一個對象管理類。《Effective》使用的是auto_ptr,C++ 11新標準中由unique_ptr,能比auto_ptr實現更多特性
在某些狀況,必須直接訪問原始資源。例如 C API 沒有class的概念
1. 顯式。RAII提供一個成員函數,返回原始資源。如shared_ptr和unique_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就成爲相似空懸指針。
new單一對象,使用delete銷燬
new對象數組,使用delete[] 銷燬對象數組
轉換爲代碼就是
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());