在C++中抛出后是否会调用析构函数?

Luc*_*ore 43 c++ exception-handling raii try-catch

我运行了一个示例程序,确实调用了堆栈分配对象的析构函数,但这是否由标准保证?

NPE*_*NPE 59

是的,它是有保证的(如果捕获了异常),直到调用析构函数的顺序:

C++ 11 15.2构造函数和析构函数[except.ctor]

1当控件从throw-expression传递到处理程序时,将为输入try块后构造的所有自动对象调用析构函数.自动对象按照完成构造的相反顺序销毁.

此外,如果在对象构造期间抛出异常,则保证部分构造的对象的子对象被正确销毁:

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

整个过程称为"堆栈展开":

3为从try块到throw-expression的路径构造的自动对象调用析构函数的过程称为"堆栈展开".如果在堆栈展开期间调用的析构函数以异常退出,则调用std :: terminate(15.5. 1).

堆栈展开形成了被称为资源获取初始化(RAII)的广泛使用的技术的基础.

请注意,如果未捕获异常,则不必执行堆栈展开.在这种情况下,是否完成堆栈展开的实现.但是,无论是否完成堆栈展开,在这种情况下,您都可以保证最终调用std::terminate.

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

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

  • 注意:关于被中断的对象的构造.对象本身不被破坏(它没有住过),什么是保证的是,子部分(基类,属性)已经完全迄今建造将按照相反的顺序被销毁. (7认同)

小智 7

是的,保证在堆栈展开时调用析构函数,包括因抛出异常而展开.您必须记住的例外情况很少:

  • 如果在其构造函数中抛出异常,则不会调用该类的析构函数.
  • 如果在构造初始化列表catch块中捕获异常,则会自动重新抛出异常.

  • 3)析构函数应该*从不*抛出异常,因为没有*方法来充分处理它们. (9认同)
  • @ AlfP.Steinbach:*"使用代码可以围绕它设计."* - 我同意.但是,经验法则是您的对象可能会在您未预见的上下文中使用,并且无法主动满足.例如,如果你将析构函数抛出的对象放在一个STL向量中,并且该向量由于一些不相关的异常堆栈展开而被破坏,它将调用你的对象的析构函数,你的应用程序就会变成"poof".当然我可以把针从手榴弹上取下来,小心地把它拿一会儿然后把针放回去.但是我从来没有*应该这样做...... (4认同)
  • @DevSolar确实存在一些反例. (3认同)
  • @DevSolar:你(故意?)不清楚你想要反对的例子.但是关于第一个声明,"析构函数永远不应该抛出异常",一个不完全不常见的反例是一个表示函数结果的对象,如果调用者代码没有检查它是否代表失败,它会从析构函数抛出.另一个例子是一个事务保护对象,它从析构函数抛出,除非它嵌入的代码已成功完成(例如转移某些东西的所有权)并调用它的`release`方法. (2认同)