2017-08-20 44 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