天天看點

[Effective C++ --014]在資源管理類中小心copying行為

第一節 <背景>

條款13中講到“資源取得的時機便是初始化時機”并由此引出“以對象管理資源”的概念。通常情況下使用std中的auto_ptr(智能指針)和tr1::shared_ptr(引數智能指針)作為管理資源的對象。

事實上,這種管理方法十分有效。但是,auto_ptr和tr1::shared_ptr隻能管理基于堆(heap-based)的資源,而非heap-based的資源卻往往不适合。

是以,有的時候你需要建立自己的資源管理類。本文介紹的内容是在你建立自己的資源管理類時應該注意的事項。

第二節 <正文>

我們知道在C API中處理Mutex的互斥對象,有lock何unlock兩個函數可用:

void lock(Mutex* pm);            // 鎖定pm指向的互斥量
  void unlock(Mutex* pm);          // pm指向的互斥量解鎖      

假設我們寫了Lock類來管理鎖。

class  Mutex{
public:
    Mutex():Count(0){}
public:
    int Count;
};
void lock(Mutex* pm){pm->Count++;}
void unlock(Mutex* pm){pm->Count--;}
class Lock
{
public:
    explicit Lock(Mutex* pm):mutexPtr(pm)
    {lock(mutexPtr);}          // 将mutexPtr指向的互斥變量加鎖
    ~Lock(){unlock(mutexPtr);} // 将mutexPtr指向的互斥變量解鎖
private :
    Mutex * mutexPtr;
};      

上面代碼滿足RAII(Resource Acquisition is Initialization)原則即,資源在擷取時既是初始化時,失去時既是清理時。

想象下面的場景時,程式的輸出結果是什麼。

1     Mutex m;
2     cout << "Mutex is " << m.Count << endl;
3     Lock m1(&m);
4     cout << "Mutex is " << m.Count << endl;
5     Lock m2(m1);
6     cout << "Mutex is " << m.Count << endl;
7     m1.~Lock();
8     cout << "Mutex is " << m.Count << endl;      

輸出結果為:

Mutex is 0
Mutex is 1
Mutex is 1
Mutex is 0      

這是為什麼呢?前兩個0和1輸出無可厚非,第三個的輸出為拿m1作為執行個體對象去指派給m2,操作對象為m1,不會直接影響m;第四個互斥量m的管理者m1被銷毀了,那麼m也就被解鎖了。

在上面的例子中,m的值不斷被變更,顯然,這種資源的管理的方式是不合理的。

可能的解決方法:

1.禁止複制。禁止複制的做法具體的可參照條款6的說明。

class UnCopyable {
public:
     UnCopyable(){}
private:
     UnCopyable(const UnCopyable& ths) {
     }
};
class Lock:private UnCopyable {
    ...
}      

2.使用引用計數智能指針:tr1::shared_ptr。

從條款13我們已經知道引用計數智能指針會跟蹤使用該資源的所有對象數,計數為0時,資源會被删除。注意,這裡删除互斥量m不是我們所期待的,我們期待是解鎖互斥量

幸運的是tr1::shared_ptr允許自定義所謂的“删除”動作,該動作是在計數為0時執行的。于是類Lock可以是下面的樣子。

class Lock
{
public:
     explicit Lock(Mutex* pm):mutexPtr(pm,unlock)
     {lock(mutexPtr.get());}          // 将mutexPtr指向的互斥變量加鎖
private :
     shared_ptr<Mutex> mutexPtr;
};      

有沒有發覺貌似少了點東西?對,析構函數沒有了。因為share_ptr會幫你完成這一工作。

3.複制管理對象時也複制所管理的資源。

請回頭想一個問題:為什麼需要自己的資源管理類?那麼,可能的理由是當不需要某個資源時,資源能被正常釋放(删除,其他動作)。資源存在多個複件并不可怕,可怕的是複件在該銷毀的時候卻沒有銷毀。也就是,管理對象與所管理的資源要一一對應。為了保證這種對應關系,在複制管理對象時也複制所管理的資源。

4.轉移資源的管理權。

在某些特殊場合下,你可能希望資源隻被一個對象擁有,也就是管理對象在copying時要進行資源所有權的轉移。從條款13中講到的auto_ptr可以完美的實作這個需求。

■總結

1.複制管理對象時,請一并複制對象所管理的資源,資源的copy行為決定了管理對象的copy行為

2.普遍的RAII class的copy行為是抑制複制,使用引用計數

轉載于:https://www.cnblogs.com/hustcser/p/4103527.html