TIL,我对RAII&的'inter-twining'(缺乏更好的词)的看法stack-unwinding是完全(如果不是完全)错误的。我的理解是使用RAII,防止任何/所有资源泄漏 - 甚至是可能由未处理的异常引起的泄漏。
然而写this test program并随后在this article/documentation上磕磕绊绊,让我意识到堆栈展开只会导致启用RAII的资源解除分配在try块中自动启动而不是自动启动,比如,外/其他范围。
我对这个(新的)理解是否正确?或者还有其他细微差别我尚未掌握?那里的任何大师都想要进入?任何好的写作/分析/解释(堆栈展开)的指针都会有所帮助/赞赏......
答案 0 :(得分:5)
来自C ++ 03标准,§15.3/ 9:
如果在程序中找不到匹配的处理程序,则调用函数terminate();在调用terminate()之前是否展开堆栈是实现定义的(15.5.1)。
§15.5.1/ 1:
在下列情况下,必须放弃异常处理以获得不那么微妙的错误处理技术:...当异常处理机制找不到抛出异常的处理程序时(15.3)......
§15.5.1/ 2:
在这种情况下,
void terminate();
被称为(18.6.3)。在没有找到匹配处理程序的情况下,无论堆栈是否在调用terminate()之前展开,它都是实现定义的。在所有其他情况下,在调用terminate()之前不应解开堆栈。根据判断展开过程最终会导致调用终止(),不允许实现过早地完成堆栈展开。
答案 1 :(得分:2)
从throw some_exception
到catch(some_exception)
的路上发生“堆栈展开”是正确的。如果您的例外从未达到过捕获,我们就不知道会发生什么。
这是一个大问题吗?正如您自己展示的那样,您只需要在某处添加catch(...)
以捕获所有可能的异常,问题就会消失。
答案 2 :(得分:0)
标准定义了三种结束C ++程序执行的方法:
main
返回。具有自动存储(功能本地)的对象已被销毁。具有静态存储(全局,类静态,函数静态)的对象将被销毁。std::exit
的<cstdlib>
。具有自动存储的对象 NOT 已销毁。具有静态存储的对象将被销毁。std::abort
的<cstdlib>
。具有自动和静态存储的对象 NOT 已销毁。同样相关的是来自std::terminate
的{{1}}。可以使用<exception>
替换terminate
的行为,但std::set_terminate
必须始终通过调用terminate
或某些类似的特定于实现的替代方案来“终止程序的执行”。默认值只是abort
。
{ std::abort(); }
。例如,来自堆栈展开调用的析构函数的异常或来自静态存储对象构造函数或析构函数的异常。在这些情况下,没有(更多)堆栈展开。
当找不到匹配的std::terminate
处理程序时,C ++也会调用std::terminate
。在这种情况下,在调用catch
之前,C ++可以选择展开到main
。因此,您的示例可能会使用不同的编译器产生不同的结果。
因此,如果您正确使用RAII,那么“防止”程序的其余步骤是:
terminate
。std::abort
,要么避免所有具有静态存储持续时间的对象。std::exit
中添加catch (...)
处理程序,并确保在其中或之后不会发生任何分配或异常。main
的其他编程错误。
std::terminate
规范一样,这意味着即使它们没有要调用的析构函数,也不能抛出异常“。”