2017-08-20 76 views
0

讓我們假設以下簡單的例子:堆棧展開如何處理析構函數調用?

#include <iostream> 

struct foo { 
    ~foo() { 
     std::cout << "~foo()" << std::endl; 
    } 
}; 

struct bar { 
    foo x; 
    bar() : x() { 
     throw -1; 
    } 
    ~bar() { 
     std::cout << "~bar()" << std::endl; 
    } 
}; 

struct baz { 
    ~baz() { 
     std::cout << "~baz()" << std::endl; 
    } 
}; 

int main() { 
    try { 
     baz y; 
     bar z; 
    } // Destructor is called for everything fully constructed up to here? 
    catch(...) { 
    } 
} 

輸出是

~foo() 
~baz() 

所以很明顯bar的析構函數沒有被調用。

這意味着什麼樣的資源分配將被髮布在bar的析構函數中?

E.g.

struct bar { 
    CostlyResource cr; 
    bar() { 
     cr.Open(); // Aquire resource 

     // something else throws ... 
     throw -1; 
    } 
    ~bar() { 
     if(cr.IsOpen()) { 
      cr.Release(); // Free resource 
     } 
    } 
}; 

對於異常安全的實施緣故我能做些什麼,以確保bar的資源成員正常釋放?

回答

2

爲了實現異常安全,我該怎麼做才能確保酒吧的資源成員被正確釋放?

您可以catch,處理和重新拋出在你的構造一個匿名excption:

struct bar { 
    CostlyResource cr; 
    bar() { 
     try { // Wrap the whole constructor body with try/catch 
      cr.Open(); // Aquire resource 

      // something else throws ... 
      throw -1; 
     } 
     catch(...) { // Catch anonymously 
      releaseResources(); // Release the resources 
      throw; // Rethrow the caught exception 
     } 
    } 
    ~bar() { 
     releaseResources(); // Reuse the code ro release resources 
    } 
private: 
    void releaseResources() { 
     if(cr.IsOpen()) { 
      cr.Release(); // Free resource 
     } 
    } 
}; 

看到完整的示例代碼here請。


因爲這是常見的在構造函數中完成動態內存分配一樣

class MyClass { 
    TypeA* typeAArray; 
    TypeB* typeBArray; 
public: 
    MyClass() { 
     typeAAArray = new TypeA[50]; 
     // Something else might throw here 
     typeBAArray = new TypeB[100]; 
    } 
    ~MyClass() { 
     delete[] typeAAArray; 
     delete[] typeBAArray; 
    } 
}; 

最簡單的方式有問題過來這或者是使用適當的容器(例如std::vector<TypeA>std::vector<TypeB>),或一個智能指針(例如std::unique_ptr<TypeA[50]>)。

1

對象的生命週期直到構造函數完成纔開始。如果從構造函數中拋出一個異常,那麼該對象的析構函數將不會被調用。當然,任何已經構建好的子對象都會按照與第一個例子中~foo()的出現相反的順序被破壞。

第二個示例中的代碼不是異常安全的。 CostlyResource設計不佳 - 它自己的析構函數應該釋放資源。那麼你的代碼將是正確的。

如果您有與現有一類,沒有清理本身正確,那麼你應該做的包裝,做,例如:

struct SafeCostlyResource : CostlyResource 
{ 
    ~SafeCostlyResource() 
    { 
     if (IsOpen()) 
      Release(); 
    } 
}; 

,並使用了隨着cr類型。 (注意 - 這是說明性的僞代碼,有幾種方法可以解決這個問題)。

+0

感謝您的額外輸入。我自己回答了這個問題,因爲我最近幾次看過這個話題。對於規範我嘗試了一次,我知道:-P ... – user0042

+0

@ user0042你在回答中提出的解決方案將會是我的首選解決方案清單。另一個選擇是具有定製刪除器的'unique_ptr'。 –

+0

至少我提到了智能指針,我很清楚你的擔憂。 – user0042