RAII 和堆栈展开
直到我对 RAII 和 RAII 的“相互缠绕”(因为缺乏更好的词)的概念为止。堆栈展开是完全(如果不是完全)错误的。我的理解是,使用 RAII 可以防止任何/所有资源泄漏 - 即使是可能由未处理的异常引起的泄漏。
然而,编写这个测试程序并随后偶然发现这篇文章/文档,让我意识到堆栈展开只会导致启用 RAII 的资源释放在 try 块内自动启动,而不是在外部/其他范围内自动启动。
我的这个(新)理解正确吗?或者还有哪些细微差别我还没有掌握?有高手愿意插话吗?指向任何好的文章/分析/解释(堆栈展开)的指针都会有帮助/赞赏......
TIL that my notions of the 'inter-twining' (for the lack of a better word) of RAII & stack-unwinding are/were quite(if not completely) wrong. My understanding was that using RAII, guarded against any/all resource leaks - even ones potentially caused by unhandled exceptions.
However writing this test program and subsequently stumbling upon this article/documentation, made me realize that stack unwinding would only cause the RAII-enabled resource deallocation to kick in for automatic's within the try block as opposed to automatic's in, say, outer/other scopes.
Am I correct in this (new) understanding? Or are there further nuances I am yet not grasping? Any gurus out there want to chime in? Pointers to any good write-ups/analyses/explanations (of stack-unwinding) would be helpful/appreciated…
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
来自 C++03 标准,§15.3/9:
§15.5.1/1:
§15.5.1/2:
From the C++03 standard, §15.3/9:
§15.5.1/1:
§15.5.1/2:
你是对的,“堆栈展开”发生在从
throw some_exception
到catch(some_exception)
的过程中。如果你的异常从未到达 catch,我们不知道会发生什么。这是一个大问题吗?正如您所展示的,您只需在某处添加一个
catch(...)
来捕获所有可能的异常,问题就会消失。You are right that "stack unwinding" happens on the way from the
throw some_exception
tocatch(some_exception)
. If your exception never reaches a catch, we don`t known what happens.Is that a big problem? As you have shown yourself, you just have to add a
catch(...)
somewhere to catch all possible exceptions, and the problem goes away.该标准定义了三种结束 C++ 程序执行的方法:
main
返回。具有自动存储(功能本地)的对象已经被销毁。具有静态存储(全局、类静态、函数静态)的对象将被销毁。
中的std::exit
。自动存储的对象不会被销毁。具有静态存储的对象将被销毁。
的std::abort
。具有自动和静态存储的对象不会被销毁。同样相关的是
中的std::terminate
。terminate
的行为可以使用std::set_terminate
替换,但terminate
必须始终通过调用来“终止程序的执行” abort
或一些类似的特定于实现的替代方案。默认值只是{ std::abort(); }
。每当抛出异常并且 C++ 无法合理地进行堆栈展开时,C++ 将调用 std::terminate。例如,来自堆栈展开调用的析构函数的异常或来自静态存储对象构造函数或析构函数的异常。在这些情况下,不会进行(更多)堆栈展开。
当找不到匹配的
catch
处理程序时,C++ 也会调用std::terminate
。在这种情况下,C++ 可以在调用terminate
之前可选展开到main
。因此,您的示例使用不同的编译器可能会产生不同的结果。因此,如果您正确使用 RAII,“防泄漏”程序的其余步骤是:
std::abort
。std::exit
要么避免所有具有静态存储持续时间的对象。catch (...)
处理程序放入main
中,并确保在其内部或之后没有发生分配或异常。std::terminate
的其他编程错误。throw()
规范一样,这意味着即使它们没有析构函数,也不能“过去”抛出异常。叫。)The Standard defines three ways to end execution of a C++ program:
main
. Objects with automatic storage (function-local) have already been destroyed. Objects with static storage (global, class-static, function-static) will be destroyed.std::exit
from<cstdlib>
. Objects with automatic storage are NOT destroyed. Objects with static storage will be destroyed.std::abort
from<cstdlib>
. Objects with automatic and static storage are NOT destroyed.Also relevant is
std::terminate
from<exception>
. The behavior ofterminate
can be replaced usingstd::set_terminate
, butterminate
must always "terminate execution of the program" by callingabort
or some similar implementation-specific alternative. The default is just{ std::abort(); }
.C++ will call
std::terminate
whenever an exception is thrown and C++ can't reasonably do stack unwinding. For example, an exception from a destructor called by stack unwinding or an exception from a static storage object constructor or destructor. In these cases, there is no (more) stack unwinding done.C++ will also call
std::terminate
when a matchingcatch
handler is not found. In this single case, C++ may optionally unwind tomain
before callingterminate
. So your example might have different results with a different compiler.So if you use RAII correctly, the remaining steps to "leak-proof" your program are:
std::abort
.std::exit
or avoid all objects with static storage duration.catch (...)
handler inmain
, and make sure no allocations or exceptions happen in or after it.std::terminate
.throw()
specification, meaning that exceptions cannot be thrown "past" them even though they have no destructors to be called.)