0

直到我对 RAII 和堆栈展开的“相互缠绕”(因为没有更好的词)的概念是/完全(如果不是完全)错误的。我的理解是,使用 RAII 可以防止任何/所有资源泄漏——甚至可能是由未处理的异常引起的。

然而,编写这个测试程序并随后偶然发现这篇文章/文档,让我意识到堆栈展开只会导致启用 RAII 的资源释放在 try 块内启动,而不是自动在外部/其他范围内.

我对这个(新的)理解是否正确?还是我还没有掌握更多的细微差别?有哪位大师想插话吗?任何好的文章/分析/解释(堆栈展开)的指针都会有所帮助/赞赏......</p>

4

3 回答 3

5

来自 C++03 标准,§15.3/9:

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

§15.5.1/1:

在以下情况下,必须放弃异常处理以使用不太微妙的错误处理技术: ...当异常处理机制找不到抛出异常的处理程序时 (15.3) ...

§15.5.1/2:

在这种情况下,

    无效终止();

称为(18.6.3)。在没有找到匹配处理程序的情况下,在调用 terminate() 之前是否展开堆栈是实现定义的。在所有其他情况下,在调用 terminate() 之前不应展开堆栈。基于展开过程最终将导致调用 terminate() 的确定,不允许实现过早地完成堆栈展开。

于 2011-04-05T19:50:49.443 回答
2

你是对的,“堆栈展开”发生在从throw some_exceptionto的路上catch(some_exception)。如果您的异常从未被捕获,我们不知道会发生什么。

这是个大问题吗?正如您向自己展示的那样,您只需添加一个catch(...)地方来捕获所有可能的异常,问题就会消失。

于 2011-04-05T20:08:36.903 回答
0

该标准定义了三种结束 C++ 程序执行的方法:

  • main. 具有自动存储(功能本地)的对象已被销毁。具有静态存储(全局、静态类、静态函数)的对象将被销毁。
  • std::exit<cstdlib>. 具有自动存储功能的对象不会被销毁。具有静态存储的对象将被销毁。
  • std::abort<cstdlib>. 具有自动和静态存储的对象不会被破坏。

同样相关的是std::terminate来自<exception>. 的行为terminate可以使用 替换std::set_terminate,但必须始终通过调用或一些类似的特定于实现的替代方法terminate来“终止程序的执行” 。abort默认只是{ std::abort(); }.

std::terminate每当抛出异常并且 C++ 无法合理地进行堆栈展开时,C++ 都会调用。例如,堆栈展开调用的析构函数的异常或静态存储对象构造函数或析构函数的异常。在这些情况下,没有(更多)堆栈展开。

当找不到std::terminate匹配的处理程序时, C++ 也会调用。catch在这种情况下,C++ 可以选择main调用terminate. 因此,您的示例使用不同的编译器可能会产生不同的结果。

因此,如果您正确使用 RAII,“防泄漏”程序的其余步骤是:

  • 避免std::abort
  • 避免std::exit或避免所有具有静态存储持续时间的对象。
  • catch (...)处理程序放入 中main,并确保在其中或之后没有分配或异常发生。
  • 避免其他可能导致的编程错误std::terminate
    • (在某些实现中,使用 C 编译器编译的函数就像它们具有 C++ 的空throw()规范一样,这意味着即使它们没有要调用的析构函数,也不能“过去”抛出异常。)
于 2011-04-06T16:34:54.517 回答