繁体   English   中英

RAII和堆栈展开

[英]RAII and Stack unwinding

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

但是,编写此测试程序并随后绊倒本文/文档使我意识到,堆栈展开只会导致在尝试块内自动启用RAII的资源释放,而不是自动启用外部/其他范围。

我对这种(新的)理解是正确的吗? 还是我还没有掌握进一步的细微差别? 那里有大师想要鸣叫吗? 指向(堆栈展开时)任何良好的撰写/分析/解释的指针将是有帮助/赞赏的……

根据C ++ 03标准§15.3/ 9:

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

§15.5.1/ 1:

在以下情况下,必须放弃异常处理,以减少不太细微的错误处理技术:...当异常处理机制找不到引发异常的处理程序时(15.3)...

§15.5.1/ 2:

在这种情况下

void Terminate();

被称为(18.6.3)。 在没有找到匹配处理程序的情况下,实现是由实现定义的,在调用terminate()之前是否取消堆栈的堆栈。 在所有其他情况下,不得在调用terminate()之前解开堆栈。 根据确定展开过程最终会导致调用终止()的实现,不允许实现过早完成堆栈展开。

没错,从throw some_exceptioncatch(some_exception)发生了“堆栈展开”。 如果您的例外从未引起注意,我们将不知道会发生什么。

那是个大问题吗? 正如您所展示的,您只需要在某处添加catch(...)即可捕获所有可能的异常,问题就消除了。

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

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

<exception> std::terminate也与此有关。 可以使用std::set_terminate替换terminate行为,但是terminate必须始终通过调用abort或某些类似的实现特定的替代方法来“终止程序的执行”。 默认值为{ std::abort(); } { std::abort(); }

每当引发异常并且C ++无法合理地进行堆栈展开时,C ++就会调用std::terminate 例如,来自析构函数的异常(通过堆栈展开调用)或来自静态存储对象构造函数或析构函数的异常。 在这些情况下,将不执行(更多)堆栈展开操作。

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

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

  • 避免std::abort
  • 避免std::exit或避免所有具有静态存储持续时间的对象。
  • catch (...)处理程序放入main ,并确保在其中或之后没有任何分配或异常发生。
  • 避免可能导致std::terminate的其他编程错误。
    • (在某些实现中,使用C编译器编译的函数的行为就像它们具有C ++的empty throw()规范一样,这意味着即使没有要调用的析构函数,也不能将异常“粘贴”给它们。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM