條款14:在資源管理類中當心copying行爲

請牢記:函數

一、複製RAII對象必須一併複製它所管理的資源,因此資源的copying行爲決定RAII對象的copying行爲。spa

二、廣泛常見的RAII class copying行爲是:抑制copying、施行引用計數法。不過其餘行爲也可能被實現。指針

 

auto_ptr和tr1::shared_ptr的觀念表如今heap_based資源上。然而並不是全部資源都是heap_based,對於非heap_based資源而言,須要創建本身的資源管理類。對象

假設咱們使用C API函數出來類型爲Mutex的互斥器對象(mutex objects),共有lock和unlock兩個函數。blog

void lock(Mutex* pm);		//鎖定pm所指的互斥器
void unlock(Mutex* pm);		//將互斥器解除鎖定

爲確保毫不會忘記將一個被鎖住的Mutex解鎖,建立一個Lock class 來管理機鎖。這樣的class的基本結構由RAII守則支配,也就是「資源在構造期間得到,在析構期間釋放」:內存

class Lock
{
public:
	explicit Lock(Mutex* pm)
		: mutexPtr(pm)
	{
		Lock(mutexPtr);		//得到資源
	}
	~Lock()
	{
		unlock(mutexPtr);	//釋放資源
	}
private:
	Mutex *mutexPtr;
};

客戶對Lock的用法符合RAII方式:ci

Mutex m;
...
{
Lock ml(&m);    //鎖定互斥器
...
}   //在區塊末尾,自動解除互斥器鎖定

若是此時Lock對象被複制:資源

Lock ml1(&m);    //鎖定m
Lock ml2(ml1);   //將ml1複製到ml2上,會發生什麼?

可能有如下兩種選擇:字符串

禁止複製:許多時候容許RAII對象複製並不合理:get

 

若是沒有按須要定義複製構造函數和賦值操做符,那麼獲得的結果一般是:非內存資源被建立一次,釋放屢次。
禁止方式:將copying操做聲明爲private。

class Lock : private Uncopyable  //禁止複製。見條款6
{ public: ...            //如前 };

若是須要複製,從新定義複製構造函數和賦值操做符是必須的。
怎麼寫它們是一個問題,具體的方案依賴於實際的須要,可使用深拷貝,也可使用相似於 shared_ptr 的引用計數機制,或者傳遞全部權。

對底層資源採用引用計數法

複製RAII對象時,將該資源的「被引用數」遞增。例:shared_ptr。

shared_ptr的缺省行爲是「當引用次數爲0時刪除所指物」,可是上面的例子咱們想要的動做是解除lock而非刪除。
好在shared_ptr容許指定「刪除器」:

class Lock
{
public:
    explicit Lock(Mutex* pm):mutexPtr(pm,unlock)  //以某個mutex初始化shared_ptr,並以unlock函數爲刪除器
    {lock(mutexPtr.get());}               //條款15

    ~Lock(){unlock(mutexPtr);}           //釋放資源
private:
    std::tr1::shared_ptr<Mutex> mutexPtr;
};

深度拷貝:
某些標準字符串類型是「指向heap內存」之指針構成。這樣的字符串對象被複制,不論指針或其所指內存都會被製做出一個復件。這樣的字符串展示深度複製行爲。

轉移底部資源的擁有權:

auto_ptr奉行的複製意義:RAII對象被複制,資源的擁有權從被複制物轉移到目標物。

相關文章
相關標籤/搜索