2016-10-10 19 views
18

关于堆栈展开,C++标准表示:是堆栈展开与C++标准保证的异常?

例外,被认为是完成了异常对象的初始化后未捕获的([except.throw]),直到完成处理程序的激活为异常([ except.handle])。这包括堆栈展开。

par 15.5.3的当前标准。我试图理解最新的句子(This includes stack unwindings)是指什么:

  • 是否它假定编译器必须关心展开堆栈?
  • 或者,它是说它是依赖于编译器来解放还是不是堆栈?

问题源于下面的代码片段:

#include <iostream> 
#include <exception> 

struct S{ 
    S() { std::cout << " S constructor" << std::endl; } 
    virtual ~S() { std::cout << " S destructor" << std::endl; } 
}; 

void f() { 
    try{throw(0);} 
    catch(...){} 
} 

void g() { 
    throw(10); 
} 

int main() { 
    S s; 
    f(); 
    //g(); 
} 

现在:

  1. ,如果你运行它,是(捕获异常),你有堆栈展开的提示
  2. 如果您对f();发表评论并取消注释g();(没有捕捉到该例外情况),则表示您没有解开堆栈的提示

因此,这两个实验似乎赞成上面的第一个项目符号; clang ++和g ++都同意结果(但它不是判别式)。

而且,在我看来很奇怪的标准,这是在指定和时间在这里留下了阴影对象现场时非常小心。

有人可以澄清吗?对于标准保证的未捕获异常,堆栈展开?如果是,在哪里?如果不是,为什么?

+1

在发布的程序中,'s'在堆栈展开期间不会被破坏。它在'f()'返回后被销毁。 – aschepler

回答

29

堆栈展开的标准保证未捕获的异常?

堆栈开卷是保证发生仅捕获异常([except.handle]/9):

如果没有发现匹配的处理器,函数std::terminate()被调用;在调用std::terminate()之前,堆栈是否展开是实现定义的。

所以它是实现定义的,否则。

如果不是,为什么?

如果发生未捕获的异常,则会调用标准std::terminate。这代表程序执行的结束。如果您当时有一些特定于平台的记录系统状态信息的平台特定方式,则可能不希望该状态受堆栈解除打扰。

如果你不这样做...那么你不在乎任何方式。

如果您确实需要堆栈始终解除堆栈,那么您可以将main代码(以及任何线程函数)置于try {} catch(...) {throw;}块中。

+0

_“如果你真的需要堆栈总是解开,那么你可以把你的主代码放在try {} catch(...){throw;}块中。”_ - 线程呢?我的直觉认为,在这种情况下单个线程入口函数也需要try-catch块,否? –

+0

@JohannGerell:我认为这是UB,如果线程函数退出并出现异常。 –

+1

@KerrekSB不,它是“终止”。 –