1. 程式人生 > >Effective C++ 條款14 在資源管理類中小心copying行為

Effective C++ 條款14 在資源管理類中小心copying行為

1. 條款13中講到“資源取得的時機便是初始化時機”並由此引出“以物件管理資源”的概念,資源會在不需要的時刻被銷燬。通常情況下使用std中的auto_prt(智慧指標)和tr1::shared_ptr(引數智慧指標)作為資源管理的物件,這種做法通常都十分有效。但是,auto_ptr和shared_ptr只能管理基於堆(heap-based)的資源,而非heap-base的資源往往不合適。因此有必要建立自己的資源管理類。

2.在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)原則即,資源在獲取時既是初始化時,失去時既是清理時。 想象下面的場景時,程式的輸出結果是什麼。

     Mutex m;
     cout << "Mutex is " << m.Count << endl;
     Lock m1(&m);
     cout << "Mutex is " << m.Count << endl;
     Lock m2(m1);
     cout << "Mutex is " << m.Count << endl;
     m1.~Lock();
     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行為是抑制複製,使用引用計數