auto_ptr和tr1::shared_ptr的觀念表現在heap_based資源上。然而並非所有資源都是heap_based,對於非heap_based資源而言,需要建立自己的資源管理類。
假設我們使用C API函數出來類型為Mutex的互斥器對象(mutex objects),共有lock和unlock兩個函數。
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方式:
Mutex m; ... { Lock ml(&m); //鎖定互斥器 ... } //在區塊末尾,自動解除互斥器鎖定
如果此時Lock對象被復制:
Lock ml1(&m); //鎖定m Lock ml2(ml1); //將ml1復制到ml2上,會發生什麼?
可能有以下兩種選擇:
禁止復制:許多時候允許RAII對象復制並不合理:
如果沒有按需要定義復制構造函數和賦值操作符,那麼得到的結果通常是:非內存資源被創建一次,釋放多次。
禁止方式:將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對象被復制,資源的擁有權從被復制物轉移到目標物。