1. 程式人生 > >C++進階--RAII 資源獲取即初始化

C++進階--RAII 資源獲取即初始化

//############################################################################
/* 資源獲取即是初始化 (RAII)
 * 
 * 使用物件來管理資源: (利用棧回退時一定會釋放棧上物件的機制)
 *  記憶體,硬體裝置,網路控制代碼等
 */

Mutex_t   mu = MUTEX_INITIALIZER;
 
void functionA()
{
   Mutex_lock( &mu );
   ... // 做一系列事情
   Mutex_unlock( &mu );      // 是否一定會被執行
}


/*
 * 解決方法:
 */ 
class Lock {
   privat:
      Mutext_t* m_pm;
   public:
      explicit Lock(Mutex_t *pm) { Mutex_lock(pm); m_pm = pm;};
      ~Lock() { Mutex_unlock(m_pm); };
}

void functionA()
{
   Lock mylock(&mu);
   ...  // 做一系列事情
}  // mutex總是會被釋放,當myloack物件從棧上被銷燬的時候


/* 結論:
 *
 * 在異常丟擲後唯一保證會被執行的程式碼是
 * 棧上物件的解構函式
 *
 * 所以資源管理要和合適的物件生命週期結合在一起
 * 從而達到自動去分配和回收的目的
 */


/* Note 1:
 * 另一個RAII的例子:  tr1:shared_ptr
 */
int function_A() {
   std::tr1::shared_ptr<dog> pd(new dog()); 
   ...
} // 當pd走出作用域時dog會被銷燬 (沒有指標指向pd).


// Note 2:
// 另一個例子:

class dog;
class Trick;
void train(tr1::shared_ptr<dog> pd, Trick dogtrick);
Trick  getTrick();

int main() {
   // tr1::shared_ptr<dog> pd(new dog());
   train(tr1::shared_ptr<dog> pd(new dog()), getTrick());
}
//問題: 上面程式碼有什麼問題?

// 引數傳遞的操作順序由編譯器決定
// 如果train()函式的引數傳遞按如下順序進行會怎麼樣:
// 1. new dog();
// 2. getTrick();
// 3. construct tr1::shared_ptr<dog>.

// 第2步如果丟擲異常,記憶體洩漏
// 結論:不要將物件存到共享指標的操作跟其他宣告混到一起

/* Note 3:
   如果資源的管理物件被拷貝會怎樣?
*/
   Lock L1(&mu);
   Lock L2(L1);

/* Solution 1: 
 * 禁止拷貝. 見【不讓編譯器生成類函式】
 */

/* Solution 2:
 * 使用tr1::shared_ptr對資源進行引用計數
 */

template<class Other, class D> shared_ptr(Other * ptr, D deleter);

// D的預設值是"delete":
    std::tr1::shared_ptr<dog> pd(new dog());

class Lock {
   private:
      std::tr1::shared_ptr<Mutex_t> pMutex;
   public:
      explicit Lock(Mutex_t *pm):pMutex(pm, Mutex_unlock) { 
         Mutex_lock(pm); 
      // The second parameter of shared_ptr constructor is "deleter" function.
      }; 
 }
}

   Lock L1(&mu);
   Lock L2(L1);