如果构造函数抛出异常会怎样? [英] What happens if a constructor throws an exception?

查看:145
本文介绍了如果构造函数抛出异常会怎样?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

那么我们会得到UB吗?

Will we get UB then?

我尝试过:

#include <iostream>

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

struct A
{
    B b;
    A(){ std::cout << "A()" << std::endl; throw std::exception(); }
    ~A(){ std::cout << "~A()" << std::endl; }
};

int main()
{
    A a;
}

A 和 B

实际输出:

B()
A()
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
bash: line 7: 21835 Aborted                 (core dumped) ./a.out

http://coliru.stacked-crooked.com/a/9658b14c73253700

因此,构造函数在任何时候抛出

So any time the constructor throws during initialization of block scope variables, do we get UB?

推荐答案

不,抛出异常是发出错误的最佳方式在对象构建过程中。 (因为没有返回值,所以除了构造一个无头对象(没有C ++中的不良样式)之外,没有其他方法。)

No, throwing an exception is the best way to signal an error during object construction. (Since there's no return value, there's no other way, other than constructing a headless object, which is bad style in C++.)

来自该人本人Bjarne Stroustrup: http://www.stroustrup.com/bs_faq2.html#ctor-exceptions

From the man himself, Bjarne Stroustrup: http://www.stroustrup.com/bs_faq2.html#ctor-exceptions

回复:但是我的析构函数没有被调用

Re: "But my destructor was not called"

确实如此。
在C ++中,对象的生存期据说是在构造函数运行完成时开始的。当析构函数被调用时,它就结束了。如果ctor抛出,则不会调用dtor。

Indeed. In C++ the lifetime of an object is said to begin when the constructor runs to completion. And it ends right when the destructor is called. If the ctor throws, then the dtor is not called.

(但任何成员变量对象的dtor,其ctor在 this 之前已经运行完毕

(But dtors of any member variable objects, whose ctors already ran to completion before this ctor ran, are called.)

您应查阅该标准或一本 good 教科书,以获取更多详细信息,尤其是。与涉及继承时发生的情况有关。根据一般经验,析构函数的调用顺序与构造相反。

You should consult the standard, or a good textbook for more details, esp. related to what happens when inheritance is involved. As a general rule of thumb, destructors are called in the reverse order of construction.

您的问题是为什么在特定代码中未调用〜B,这是因为您不会在main中捕获异常。如果更改代码以使main捕获异常,则将调用〜B()。但是,当抛出一个没有捕获的异常时,实现可以自由地终止程序而无需调用析构函数或破坏静态初始化的对象。

Your question about why "~B" was not called in your specific code, it's because you do not catch the exception in main. If you change your code so that main catches the exception, then "~B()" will be called. But, when an exception is thrown which has no catch, the implementation is free to terminate the program without calling destructors or destroying statically initialized objects.

C ++ 11中的参考标准(重点是我的):

Reference in C++11 standard (emphasis mine):


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

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

1
在某些情况下,必须放弃异常处理,以减少不太细微的错误处理技术。

1 In some situations exception handling must be abandoned for less subtle error handling techniques.

...

2
在这种情况下,将调用std :: terminate() (18.8.3)。 在没有找到匹配处理程序的情况下,由实现定义是否在调用std :: terminate()之前取消堆栈的堆栈。

作为注释,通常使用gcc和clang,在示例程序中始终会调用〜B MSVC,不会调用〜B 。异常处理非常复杂,并且该标准允许编译器编写者可以尝试并选择他们认为在这方面最好的实现,但是他们不能选择给出未定义的行为。

As a side note, generally speaking with gcc and clang, ~B will be called anyways in your example program, while with MSVC, ~B will not be called. Exception handling is complex and the standard allows that compiler writers can experiment with and choose what implementation that they think is best in this regard, but they cannot choose to give undefined behavior.

如果即使在这种情况下调用析构函数对您的程序来说也很重要,则应确保在 main 中捕获异常,以便代码可移植(工作在所有符合条件的编译器上都相同)。例如:

If it's really important for your program that the destructors are called even in this case, then you should make sure to catch exceptions in main so that your code will be portable (work the same on all conforming compilers). For example:

int main() {
    try { 
        A a;
    } catch (...) {}
}

像MSVC这样的编译器必须在退出前调用 B 的析构函数。

This way, compilers like MSVC will be obligated to call the destructor of B before exiting.

这篇关于如果构造函数抛出异常会怎样?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆