條款14:在資源管理類中小心copy行為
? ? ? ? ? 當我們深入理解“資源取得時機是初始化時機(RAII)”概念,并以此作為“資源管理類”的核心時,我們可能會遇到將RAII對象復制的情況,一般有兩種情況處理這個現象:1)如果我們的RAII對象是唯一的,那么復制就不合理,因此我們應該禁止copy構造的行為,正確的處理方式:將copy函數定義為private(base class),并用derived class private繼承這個函數來阻止copy行為;? 2)使用引用計數法,即智能指針的形式來實現對所有拷貝資源的追蹤,但是智能指針的缺省行為是當引用次數為0時刪除其所指向的對象。如果我們要實現的功能不是釋放資源,這時候就應該自定義智能指針的deleter(一個函數或者函數對象),當引用次數為0時調用這個函數實現相應功能,如:
class lock{
public:
? ? ? ? ?explicit lock(mutex *pm) : mutexptr(pm,unlock) { }? //用mutex初始化shared_ptr,并以unlock函數為刪除器
private:
? ? ? ? ? ? ? ? ?std::tr1::shared_ptr<mutex>mutexptr;
};
另外,上述例子中不需要聲明析構函數,因為class 析構函數會自動調用其non-static成員變量的析構函數;3)復制底部資源,采用深度拷貝的方法;4)采用轉移底部資源擁有權的方法,也就是所謂的auto_ptr指針;
條款15:在資源管理類中提供對原始資源的訪問
? ? ? ? ? 當你需要訪問一個RAII class對象的內部原始資源時,有兩種方法可以達成目標:1)在類的內部提供一個get成員函數,用來執行顯示轉換,也就是返回類內部指針的原始復件;2)允許隱式轉換到底部原始指針;在智能指針中,重載了指針取值操作符(operator->和operator*)來實現隱式轉換;對于一般的來說,可以在類中提供一個隱式轉換函數;如:
class font{
public:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //顯示轉換函數:
? ? ? ? ? ? explicit font(fonthandle fh) : f(fh){ }? ? ? ? ? ? ? ? fonthandle get() const {? ?return f;? }
? ? ? ? ? ? ~font() { releasefont(f); }? ? ? ? ? ? ? ? ? ? ? ? ? ? //隱式轉換函數??
private:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?operator fonthandle() const {return f;}//沒太明白怎么隱式轉換的
? ? ? ? ? fonthandle f;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //operator起到什么作用?
}
//顯示轉換? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//隱式轉換
void changefontsize(fonthandle f,int newsize);? ? ? ? ??void changefontsize(fonthandle f,int newsize);
font f(getfont());? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?font f(getfont());
changefontsize ( f.get(), newfontsize);? ? ? ? ? ? ? ? ? ? ??changefontsize ( f, newfontsize);
//但隱式轉換會出一個問題
font f1(getfont());? ??
fonthandle f2=f1;? ?//原意是拷貝,結果是f1先轉化為底部的fonthandle,然后拷貝,一旦此時f1被銷毀,f2就處于指向資源被釋放的狀態(dangle)
條款16:成對使用new和delete時采取相同的方式
? ? ? ? ? ? ? ?如果你在new的表達式中使用[ ],則必須在相應的delete表達式中也使用[ ],如果沒有使用[ ],那么也一定不要在delete中使用[ ];
條款17:以單獨語句將newed對象置入智能指針中
? ? ? ? ? 對于以下代碼分析;
? ? ? ? ? ? int priority( );
? ? ? ? ? ?void processwidget(std :: tr1 :: shared_ptr<wideget>pw,int priority);
? ? ? ? ? ? //調用時? ?processwidget(new widget, priority( ));//編譯錯誤,shared_ptr構造函數是explicit類型,不允許隱式轉換
? ? ? ? ? ?//processwidget(std :: tr1 ::?shared_ptr<wideget>(new widget),priority( ) );
? ? ? ? ? ?這個函數在調用processwidget之前,編譯器必須創建代碼,完成以下三件事情:1)調用priority;2)執行new widget;3)調用 std::tr1::shared_ptr構造函數;但是1)和2)的執行順序和編譯器有關,如果先執行2)后執行1),且在執行1)的時候發生異常,那么new widget返回的指針就會失效,那么內存泄露就產生了;
? ? ? ? ? 解決上述問題的方法:使用分離語句,把創建對象和給智能指針賦初值的語句分離出來,原因是:編譯器對于“跨越語句的各項操作”沒有重新排列的自由(只有在語句內才擁有那個自由度);代碼如下:
? ? ? ? ?std::tr1::shared_ptr<widget>pw(new widget);
? ? ? ? ? processwidget(pw,priority( ));