【问题标题】:Why is the destructor of an exception called twice?为什么异常的析构函数被调用两次?
【发布时间】:2015-05-18 17:04:10
【问题描述】:

我有以下程序:

#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::~MyError\n";
    }


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

    cout << "goodbye\n";
    return 0;
}

打印以下内容:

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

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

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

【问题讨论】:

标签: c++ destructor


【解决方案1】:

如果您检测异常的复制或移动构造函数,您会发现它在处理程序之前被调用一次。有一个临时异常对象,抛出的表达式被复制/移动到其中,处理程序中的引用将绑定到这个异常对象。 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 << "goodbye\n";

【讨论】:

  • 在这种情况下是否允许 c++ 编译器执行复制省略?如果是,为什么不呢?
  • @Random832 是的,在这种情况下允许复制省略。为什么 VS 不这样做是任何人的猜测 - 也许 OP 的优化标志不能这样做?
【解决方案2】:

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

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

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

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

【讨论】:

    【解决方案3】:

    正在复制您的异常。如果您检测复制 ctor,您可以看到:

    #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::~MyError\n";
    }
    
    int main(void) {
        try {
            throw MyError("hi");
        }
        catch (MyError& exc) {
            cout << exc.what() << endl;
        }
    
        cout << "goodbye\n";
        return 0;
    }
    

    结果:

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

    【讨论】:

      猜你喜欢
      • 2021-11-06
      • 1970-01-01
      • 2011-02-07
      • 2013-12-11
      • 2017-11-03
      • 2013-01-12
      • 1970-01-01
      相关资源
      最近更新 更多