【问题标题】:Is stack unwinding with exceptions guaranteed by c++ standard?c++ 标准是否保证堆栈展开并出现异常?
【发布时间】:2017-02-19 03:41:43
【问题描述】:

关于堆栈展开,c++ 标准说:

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

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

  • 是否假定编译器必须负责展开堆栈?
  • 或者,是否展开堆栈取决于编译器?

问题出自以下sn-p:

#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++ 都同意结果(但这不是判别式)。

另外,在我看来很奇怪的是,在指定对象 live timeduration 时非常小心的标准在这里留下了阴影。

谁能解释一下?标准是否保证未捕获异常的堆栈展开?如果是,在哪里?如果不是,为什么?

【问题讨论】:

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

标签: c++ c++11 language-lawyer


【解决方案1】:

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

保证堆栈展开只发生在捕获异常([except.handle]/9):

如果没有找到匹配的处理程序,则调用函数std::terminate();在此对 std::terminate() 的调用之前是否展开堆栈是实现定义的。

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

如果不是,为什么?

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

如果你不这样做......那么你不管用哪种方式。

如果您确实需要始终展开堆栈,则可以将您的 main 代码(和任何线程函数)放在 try {} catch(...) {throw;} 块中。

【讨论】:

  • “如果你真的需要堆栈总是被展开,那么你可以将你的主代码放在一个 try {} catch(...) {throw;} 块中。” i> - 线程呢?我的直觉是,在这种情况下,单个线程入口函数也需要 try-catch 块,不是吗?
  • @JohannGerell:如果线程函数异常退出,我认为是UB。
  • @KerrekSB 不,是terminate
  • 我的程序通常以int main2(); int main() { try { return main2(); } catch(...) { global_handler(); } }之类的开头
猜你喜欢
  • 1970-01-01
  • 2017-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多