为什么异常的析构函数被调用两次

Why is the destructor of an exception called twice?

本文关键字:两次 调用 异常 析构函数 为什么      更新时间:2023-10-16

我有以下程序:

#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;
class MyError : public runtime_error
{
    public:
        MyError(string mess = "");
        ~MyError(void);
    };
    MyError::MyError(string mess) : runtime_error(mess)
    {
        cout << "MyError::MyError()n";
    }
    MyError::~MyError(void)
    {
        cout << "MyError::~MyErrorn";
    }

int main(void)
{
    try {
        throw MyError("hi");
    }
    catch (MyError& exc) {
        cout << exc.what() << endl;
    }
    cout << "goodbyen";
    return 0;
}

打印以下内容:

MyError::MyError()
MyError::~MyError
hi
MyError::~MyError
goodbye

为什么异常(~MyError())的析构函数被调用两次?

我假定throw创建了一个新对象,但是我不明白为什么要调用类析构函数

如果您检测异常的copy或move构造函数,您会发现它在处理程序之前被调用一次。有一个临时的异常对象,抛出的表达式被复制/移动到其中,处理程序中的引用将绑定到这个异常对象。c++ 14 15.1/3 +

所以你的代码的执行结果看起来像这样(伪c++):
// throw MyError("hi"); expands to:
auto tmp1 = MyError("hi");
auto exceptionObject = std::move(tmp1);
tmp1.~MyError();
goto catch;
// catch expands to:
MyError& exc = exceptionObject;
cout << exc.what() << endl;
// } of catch handler expands to:
exceptionObject.~MyError();
// normal code follows:
cout << "goodbyen";

因为您的编译器未能删除由异常处理机制管理的从临时对象到异常对象的副本。

从概念上讲,MyError("hi")创建了一个临时对象,它将在语句结束时被销毁(在异常被处理之前)。throw将抛出的值复制到其他地方,它将一直存在,直到异常被处理。如果抛出值是临时的,那么一个体面的编译器应该省略该副本并直接初始化抛出值;显然,你的编译器没有这样做。

我的编译器(GCC 4.8.1)做得更好:

MyError::MyError()
hi
MyError::~MyError
goodbye

您的异常正在被复制。如果使用了复制器,可以看到:

#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;
class MyError : public runtime_error
{
public:
    MyError(MyError const &e) : runtime_error("copy") { std::cout << "Copy MyError"; }
    MyError(string mess = "");
    ~MyError(void);
};
MyError::MyError(string mess) : runtime_error(mess) {
    cout << "MyError::MyError()n";
}
MyError::~MyError(void) {
    cout << "MyError::~MyErrorn";
}
int main(void) {
    try {
        throw MyError("hi");
    }
    catch (MyError& exc) {
        cout << exc.what() << endl;
    }
    cout << "goodbyen";
    return 0;
}
结果:

MyError::MyError()
Copy MyError
MyError::~MyError
copy.what()
MyError::~MyError
goodbye