C++ 如果构造函数抛出异常会发生什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32323406/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:07:28  来源:igfitidea点击:

What happens if a constructor throws an exception?

c++initialization

提问by stella

Will we get UB then? I tried this:

那我们会得到UB吗?我试过这个:

#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;
}

the desctructor was not called for netither Anor B. The actual output:

该desctructor没有要求netitherA也没有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

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

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

所以任何时候构造函数在块作用域变量的初始化过程中抛出,我们得到 UB 吗?

回答by Chris Beck

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++.)

不,抛出异常是在对象构造期间发出错误信号的最佳方式。(因为没有返回值,所以除了构造无头对象之外别无他法,这在 C++ 中是一种糟糕的风格。)

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

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

Re: "But my destructor was not called"

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

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.

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

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

(但是任何成员变量对象的 dtors,其 ctors 在这个ctor 运行之前已经运行完成,被调用。)

You should consult the standard, or a goodtextbook 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.

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

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.

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

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

C++11 标准中的参考(强调我的):

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

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

...

2In such cases, std::terminate() is called (18.8.3). 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.

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

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

...

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

As a side note, generally speaking with gcc and clang, ~Bwill be called anyways in your example program, while with MSVC, ~Bwill 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.

作为旁注,一般来说,gcc 和 clang~B会在您的示例程序中被调用,而对于 MSVC,~B则不会被调用。异常处理很复杂,标准允许编译器编写者进行试验并选择他们认为在这方面最好的实现,但他们不能选择给出未定义的行为。

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 mainso that your code will be portable (work the same on all conforming compilers). For example:

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

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

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

这样,像 MSVC 这样的编译器将有义务B在退出之前调用 的析构函数。

回答by Ari0nhh

Throwing exceptions in the constructor is standard way of the error handling and is not an undefined behavior. If you throw in constructor it is assumed that an object was not initialized properly, so its destructor is not called.

在构造函数中抛出异常是错误处理的标准方式,不是未定义的行为。如果你抛出构造函数,则假定对象未正确初始化,因此不会调用其析构函数。