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

发布于 2024-12-18 16:52:38 字数 45 浏览 6 评论 0原文

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

I ran a sample program and indeed destructors for stack-allocated objects are called, but is this guaranteed by the standard?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

⊕婉儿 2024-12-25 16:52:38

是的,这是有保证的(前提是捕获了异常),具体取决于调用析构函数的顺序:

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

1 当控制从 throw 表达式传递到处理程序时,将为所有对象调用析构函数
自进入 try 块以来构造的自动对象。这
自动对象按照与完成相反的顺序销毁
他们的建设。

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

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

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

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

堆栈展开构成了广泛使用的技术的基础,该技术称为资源获取即初始化(RAII)

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

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

2 ...在没有找到匹配的handler的情况下,
在调用 std::terminate() 之前是否展开堆栈是由实现定义的。

Yes, it is guaranteed (provided the exception is caught), down to the order in which the destructors are invoked:

C++11 15.2 Constructors and destructors [except.ctor]

1 As control passes from a throw-expression to a handler, destructors are invoked for all
automatic objects constructed since the try block was entered. The
automatic objects are destroyed in the reverse order of the completion
of their construction.

Furthermore, if the exception is thrown during object construction, the subobjects of the partially-constructed object are guaranteed to be correctly destroyed:

2 An object of any storage duration whose initialization or
destruction is terminated by an exception will have destructors
executed for all of its fully constructed subobjects (excluding the
variant members of a union-like class), that is, for subobjects for
which the principal constructor (12.6.2) has completed execution and
the destructor has not yet begun execution. Similarly, if the
non-delegating constructor for an object has completed execution and a
delegating constructor for that object exits with an exception, the
object’s destructor will be invoked. If the object was allocated in a
new-expression, the matching deallocation function (3.7.4.2, 5.3.4,
12.5), if any, is called to free the storage occupied by the object.

This whole process is known as "stack unwinding":

3 The process of calling destructors for automatic objects constructed
on the path from a try block to a throw-expression is called “stack
unwinding.” If a destructor called during stack unwinding exits with
an exception, std::terminate is called (15.5.1).

Stack unwinding forms the basis of the widely-used technique called Resource Acquisition Is Initialization (RAII).

Note that stack unwinding is not necessarily done if the exception is not caught. In this case it's up to the implementation whether stack unwinding is done. But whether stack unwinding is done or not, in this case you're guaranteed a final call to std::terminate.

C++11 15.5.1 The std::terminate() function [except.terminate]

2 … In the situation where no matching handler is found,
it is implementation-defined whether or not the stack is unwound before std::terminate() is called.

野味少女 2024-12-25 16:52:38

是的,保证在堆栈展开时调用析构函数,包括由于抛出异常而展开。您只需记住几个有关异常的技巧:

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

Yes, destructors are guaranteed to be called on stack unwinding, including unwinding due to exception being thrown. There are only few tricks with exceptions that you have to remember:

  • Destructor of the class is not called if exception is thrown in its constructor.
  • Exception is automatically re-thrown if caught in construction initialization list catch block.
孤独陪着我 2024-12-25 16:52:38

如果抛出异常,则 cpp 操作通常会继续。这包括析构函数和堆栈弹出。但是,如果未捕获异常,则无法保证堆栈弹出。

我的移动编译器也无法捕获裸露抛出或空抛出。

例子:

#include <Jav/report.h>

int main()
{
 try { throw; }
 catch(...) { rep("I bet this is not caught"); }
 }

If a throw is caught then normally cpp operations continue. This include destructors and stack popping. However if the exception is not caught, stack popping is not guaranteed.

Also a bare throw or empty throw cannot be caught by my mobile compiler.

example:

#include <Jav/report.h>

int main()
{
 try { throw; }
 catch(...) { rep("I bet this is not caught"); }
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文