为什么你不能从析构函数中抛出.例

Mis*_*tyD 3 c++ destructor exception

我已经读过,因为堆栈展开而抛出析构函数并不是一个好主意.我不确定我完全明白这一点.所以我尝试了以下示例

struct foo
{
    ~foo()
    {
        throw 1;
    }
};


struct bar
{
    ~bar()
    {
        throw 2;
    }
};

int main()
{
    try 
    {
        foo a;
        bar b;
        throw 3;
    }catch(int a)
    {
        std::cout << a;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我期待a将是1因为前3被抛出然后调用b的析构函数抛出2然后调用a的析构函数抛出1.显然这不是这种情况,这可能解释为什么它不是一个好的从析构函数中抛出的想法.我的问题是为什么称为b的析构函数的abort()被称为?

Sha*_*our 6

堆栈展开期间抛出异常将导致std::terminate被调用,其默认操作是调用std::abort.

CERT在他们的ERR33-CPP中有一个很好的解释.析构函数不得抛出异常文档(强调我的):

在抛出异常导致的堆栈展开期间很可能会调用析构函数.如果析构函数本身抛出异常,因为抛出异常而调用,则调用函数std :: terminate(),调用std :: abort()的默认效果.这可能为拒绝服务攻击提供机会.因此,析构函数必须满足无抛出保证,也就是说,如果它们本身被抛出异常而被调用,则它们不得抛出异常.

这在C++标准部分15.2 构造函数和析构函数草案中有所说明:

为从try块到throw-expression的路径构造的自动对象调用析构函数的过程称为"堆栈展开".如果在堆栈展开期间调用的析构函数以异常退出,则调用std :: terminate(15.5.1 ).[注意:因此析构函数通常应该捕获异常,而不是让它们从析构函数中传播出来. - 尾注]

请注意,在C++ 11中noexcept(true),只要没有调用的函数允许异常,就会隐式指定析构函数.所以在这种情况下,从析构函数中抛出就会调用std::terminate.

从部分12.4 Destructors:

没有异常规范的析构函数声明被隐式地认为具有与隐式声明相同的异常规范(15.4).

15.4说:

隐式声明的特殊成员函数(第12条)应具有异常规范.如果f是隐式声明的默认构造函数,复制构造函数,移动构造函数,析构函数,复制赋值运算符或移动赋值运算符,则其隐式异常规范指定type-id T当且仅当T被例外规范允许时才由f的隐式定义直接调用的函数; 如果它直接调用的任何函数允许所有异常,则f 允许所有异常,如果它直接调用的每个函数都不允许异常,则f 不允许异常.

从理论上讲,你可以使用std :: uncaught_exception来检测析构函数中的堆栈展开,但是在GotW#47中, Herb Sutter解释了为什么这种技术没有看起来那么有用.

尽管Herb最近在N4152中提出了修复:未捕获的_exceptions