异常后清理

时间:2013-12-05 10:14:10

标签: c++ exception

我有一个像这样的代码:

class myclass
{
     myclass()
     {
        // doing some init here
      }
     ~myclass()
     {
        // doing some important clean up here
     }
     void Do()
     {
          //    doing some work which may throw exception
      }
}

我正在以这种方式使用这个类:

MyFunction()
{
    myclass mc;
    mc.do();
 }

我的问题是:

如果do函数中有异常,会发生什么?是myclass的析构函数吗?

如果不是,处理此类情况的最佳方法是什么?假设我没有源代码,并且我确信析构函数中发生了什么。

3 个答案:

答案 0 :(得分:6)

  

如果do函数中有异常,会发生什么?

如果你有任何处理程序,它将被处理。

  

myclass的析构函数是否被调用?

是的,当然。标准引用了这个::

  

任何存储持续时间的对象,其初始化或销毁   由异常终止将为所有人执行析构函数   其完全构建的子对象(不包括变体成员)   一个类似联合的类),即对于主体的子对象   构造函数(12.6.2)已完成执行并且析构函数已执行   尚未开始执行。同样,如果是非委托构造函数   对于一个对象已经完成执行和一个委托构造函数   该对象以异常退出,该对象的析构函数将是   调用。如果对象是在new-expression中分配的,则匹配   调用deallocation函数(3.7.4.2,5.3.4,12.5),如果有的话   释放对象占用的存储空间。

整个过程称为“堆栈展开”:

  

为构造的自动对象调用析构函数的过程   在从try块到throw-expression的路径上称为“堆栈”   展开。“如果在堆栈展开期间调用的析构函数退出   一个例外,调用std :: terminate(15.5.1)。

     

C ++ 11 15.5.1 std :: terminate()函数[except.terminate]

     

2 ...在没有找到匹配处理程序的情况下,它是   实现 - 定义堆栈是否在之前解开   调用std :: terminate()。

答案 1 :(得分:3)

如果在某处捕获到异常,则堆栈展开将保证为此对象调用析构函数(通常,由于异常而超出作用域的所有自动变量) )。这是一个非常重要的保证,没有它,我们就不能使用像RAII这样的技术来编写异常安全的代码。

如果未捕获异常,则程序将被终止,并且它是否依赖于实现,无论堆栈是否将首先展开。如果即使在那种情况下彻底销毁对象也很重要,那么一个选择是在对象的范围之外捕获并重新抛出异常。

如果程序以其他方式结束,例如通过调用exit()terminate(),或接收未处理的信号,然后堆栈不会被解开。如果你在这些情况下需要干净的破坏,那么事情就会变得混乱。

答案 2 :(得分:0)

C ++没有像许多其他语言那样明确的“finally”子句,而是依赖于“RAII”,它本质上是一种使用范围“自动”变量并依赖于它们的析构函数将被调用的事实。他们走出范围的适当时刻。

在你的情况下,即使mc.do()抛出异常,也会调用myclass的析构函数。在很多这些“闭包”问题中,放在scope块顶部的对象是一个只用于在终止时调用析构函数的类。

“D”语言带有特殊的闭包语法。已经有人尝试编写用C ++编写的库,并且使用lambdas它比以前更容易编写,尽管我认为它们还不是C ++的正式部分。