Effective c++ 條款14:在資源管理類中小心copying行為
並非所有資源都是heap-based,對那種資源而言,像auto-ptr和tr1::shared_ptr這樣的智慧指標往往不適合作為資源掌管者。因此,我們偶爾需要建立自己的資源管理類。
例如,假設我們使用互斥器物件Mutex,共有lock和unlock函式可用:
void lock(Mutex* pm); // 鎖定pm所指的互斥鎖
void unlock(Mutex* px); //將互斥鎖解除鎖定
為確保絕不會忘記將一個被鎖住的Mutex解鎖,我們可能會希望建立一個class用來管理互斥鎖。這樣的class的基本結構由RAII守則支配:資源在構造期間獲得,在析構期間釋放。
class Lock {
public:
explicit Lock(Mutex* pm):mutexPtr(pm) {
lock(mutexPtr);
}
~Lock() {
unlock(mutexPtr);
}
private:
Mutex* mutexPtr;
};
然而當一個RAII物件被複制時,會發生什麼事?
大多情況下會選擇以下兩種方案:
1、對RAII class禁止複製行為
許多時候執行RAII物件被複制並不合理。如果複製動作對RAII class並不合理,我們便應該禁止他。條款6告訴了我們怎麼做:將copying操作宣告為private,或者繼承一個Uncopyable class。
2、對底層資源祭出“引用計數法”(reference-count)
有時候我們希望保有資源,直到它的最後一個使用者(某物件)被銷燬。這種情況下複製RAII物件時,應該將資源的“被引用數”遞增。shared_ptr便是如此。
通常只要內含一個shared_ptr成員變數,RAII classes便可實現reference-counting copying行為。然而shared_ptr的預設行為是“當引用次數為0時刪除其所指物”,那不是我們所要的行為。而當我們用Mutex時,我們想做的釋放動作是解除鎖定而非刪除。幸運的是shared_ptr允許指定所謂的“刪除器(deleter)”,那是一個函式或函式物件,當引用次數為0時便被呼叫(此機能並不存在與auto_ptr——它總是將其指標刪除)。刪除器對shared_ptr建構函式而言是可有可無的第二引數,所以程式碼看起來像這樣:
class Lock {
public:
explicit Lock(Mutex* pm):mutexPtr(pm) {
lock(mutexPtr.get());
}
private:
std::tr1::shared_ptr<Mutex> mutexPtr;
};
請注意,本例的Lock class不再宣告解構函式,因為沒有必要,因為解構函式(不論是編譯器生成的還是使用者自定的)會自動呼叫其non-static成員變數的解構函式,而mutexPtr的解構函式會在互斥器的引用次數為0時自動呼叫其刪除器。
注意你並沒有忘記析構,你只是依賴了編譯器生成的預設行為。
3、複製底部資源
也即複製資源管理物件時,進行的是深度拷貝。
4、轉移底部資源的擁有權
某些罕見的場合下可能希望確保永遠只有一個RAII物件指向一個未加工資源,即使RAII物件被複制依然如此。此時資源的擁有權會從被複制物轉移到目標物。(如auto_ptr和unique_ptr)