关于 C++ 中的 RAII

2023-05-16

名字起得不好,实际上是想体现资源管理和对象生命周期绑定,在构造函数里获取资源,在析构函数里释放资源。

RAII 的有趣之处在于利用栈对对象的生命周期进行管理,也就间接实现了对资源的管理,因为进入作用域时会把上下文(对应的数据结构是栈帧 stack frame)压栈,离开作用域时相应地会出栈,压栈时调用构造函数,实现了对资源的获取,出栈时调用析构函数,实现了对资源的释放。

获取资源是相对容易的,即使失败了也无非就是没有获取成功,哪怕因此导致程序退出,也不至于产生太严重的后果。而释放资源出现问题的后果可能会严重得多,可能导致“永久性”的资源泄漏,比如内核和文件系统的资源,如IPC(信号量、消息队列、共享内存)和文件。这套机制在确保资源正确释放上做出了很多的设计。

我们不妨设想一下,有哪些导致资源释放出现问题的场景?

  1. 忘记释放;
  2. 对象切片 Object slicing;
  3. 程序出现异常;
  4. 自定义内存管理机制;
  5. ……

第一种第二种是自己写法的问题,C++ 针对第一种问题提供了智能指针等解决方案,第二种问题是 copy 时的信息丢失,属于是没有处理好多态性,需要自己注意,第四种比较少见而且很难一概而论,最常见的还是异常。所以,RAII 针对异常有一套专门的机制:栈展开(stack unwinding)。

应该说这个词还是比较形象的,不停地寻找可以处理当前异常的 catch 语句,如果在当前作用域中没有找到,就出栈然后在上一级中寻找,类似于把栈一层一层展开的过程。从我个人的角度,这个机制是在程序出现异常时尽量先调用析构函数,尽可能确保把资源释放出去。在我个人的体验中,甚至在 try-catch 的过程中,甚至会先触发析构再触发 catch,把这个原则贯彻到底。

不过这个机制也不是一直有效。因为异常从本质上来说还是一种程序退出的机制,大部分时候问题还是出在程序退出机制本身。一般来说,会有这么几种程序退出的情况:

  1. main 函数 return
  2. std::terminate
  3. std::exit
  4. std::abort

然而很不幸的是,除了在 main 函数中正常退出之外,其他几种都不能确保资源完全释放。具体来说:std::exit 只释放静态区的数据,而 std::abort 则是完全不释放。为什么没有 std::terminate?因为这玩意默认调用了 std::abort。可以参考这篇文章:https://www.cnblogs.com/zhenjing/archive/2011/07/06/stackunwinding.html

这也是不建议写在析构函数中抛出异常的代码,以及在异常中抛出异常的代码的原因,因为可能会导致栈展开机制失效,从而阻碍资源完全释放。不过为什么大部分时候也没啥事呢?因为有 OS 兜底,进程退出时会把进程占用的资源释放。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于 C++ 中的 RAII 的相关文章

  • C++服务器开发100个知识要点C++RAII惯用法

    最初的写法 在笔者刚学习服务器开发的时候 xff0c 公司给笔者安排了一个练习 xff1a 在 Windows 系统上写一个 C 43 43 程序 xff0c 用该程序实现一个简单的服务 xff0c 在客户端连接上来时 xff0c 给客户端
  • C++11智能指针之unique_ptr

    1 智能指针概念 智能指针是基于RAII机制实现的类 模板 具有指针的行为 重载了operator 与operator gt 操作符 可以 智能 地销毁其所指对象 C 11中有unique ptr shared ptr与weak ptr等智
  • C++11智能指针之unique_ptr

    1 智能指针概念 智能指针是基于RAII机制实现的类 模板 具有指针的行为 重载了operator 与operator gt 操作符 可以 智能 地销毁其所指对象 C 11中有unique ptr shared ptr与weak ptr等智
  • 非词法生命周期借用检查器会提前释放锁吗?

    我读了什么是非词汇生命周期 使用非词法借用检查器 可以编译以下代码 fn main let mut scores vec 1 2 3 let score scores 0 borrows scores but never used its
  • 为什么要使用其中之一:`boost::shared_array` VS `boost::shared_ptr`?

    因此 要处理图像或类似图像的大块内存 显然有很多选择 由于我是智能指针和 RAII 的粉丝 我想知道使用它是否更智能 a shared ptr to a std vector or 去与shared array指向动态分配的数组 选择其中之
  • 如何在函数出口上运行清理代码?

    C 类提供 RAII 习惯用法 因此你不必关心异常 void function The memory will be freed automatically on function exit std vector
  • RAII 和系统资源清理

    RAII 是资源清理的一个很好的解决方案 然而 RAII 基于堆栈展开 如果进程异常终止 堆栈将不会被展开 这意味着 RAII 在这种情况下不起作用 对于进程生命周期的资源来说 这没什么好担心的 但是对于文件系统生命周期或者内核生命周期的资
  • Python 中的 RAII - 离开范围时自动销毁

    我一直在努力寻找RAII https en wikipedia org wiki Resource acquisition is initialization在Python中 资源分配即初始化是 C 中的一种模式 其中 对象在创建时就被初始
  • 为什么 C++ 标准文件流没有更严格地遵循 RAII 约定?

    为什么 C 标准库使用流open close 语义与对象生命周期分离 从技术上讲 关闭销毁可能仍会生成类 RAII 但获取 释放独立性会在范围内留下漏洞 其中句柄可以指向任何内容 但仍需要运行时检查来捕获 为什么库设计者选择他们的方法而不是
  • 控制反转和 RAII 可以一起发挥作用吗?

    我刚刚阅读了有关控制反转 IOC 的内容 这让我很困扰 它似乎让内存管理变得很痛苦 当然 ioc 似乎主要用于垃圾收集环境 Net Java Scripting 而我关心的是非 gc 设置 我在这里担心的是 IOC 在某种程度上违背了 RA
  • 术语的含义 - 资源获取就是初始化

    我知道 RAII 是做什么的 这都是为了在代码抛出异常时防止内存泄漏等 现在我想明白这句话的意思smart term http en wikipedia org wiki 收购 http en wikipedia org wiki Acqu
  • Python 中的 RAII:__del__ 有什么意义?

    乍一看 好像是Python的 del 特殊方法提供了与 C 中析构函数几乎相同的优点 但根据Python文档 https docs python org 3 4 reference datamodel html https docs pyt
  • 依赖 Windows 句柄的类型作为指针可以吗?

    Windows 句柄有时很烦人 需要记得在之后进行清理 使用创建的笔和画笔执行 GDI 就是一个很好的例子 RAII 解决方案很棒 但是为每种不同类型的手柄制作一个完整的 五规则 RAII 类真的那么好吗 当然不是 我能看到的最好的结果是一
  • 使用 c++ std::unique_ptr<> 或 std::shared_ptr<> 管理 Objective-C 对象

    Objective C 可以在某种程度上与 C 混合 可以互相打电话 https stackoverflow com q 1061005 1792701 但 Objective C 对象仍然或多或少是手动管理的 并且 RAII 习惯用法在该
  • 是否有一个 C++ 标准类可以在作用域退出时将变量设置为值

    在成员函数的范围内 我想临时将成员变量设置为某个值 然后 当这个函数返回时 我想将此成员变量重置为给定的已知值 为了避免异常和多次返回 我用一个简单的 RAII 类来完成它 它是在成员函数的范围内定义的 void MyClass MyMem
  • 当本机 (C++) 异常传播到 CLR 组件时不会调用析构函数

    我们有大量的本机 C 代码 已编译成 DLL 然后我们有几个包含 C CLI 代理代码的 dll 来包装 C 接口 最重要的是 我们有 C 代码调用 C CLI 包装器 标准的东西 到目前为止 但在很多情况下 本机 C 异常被允许传播到 N
  • 计算出的“goto”是否尊重 C++ 对象的生命周期?

    Regular goto在 C 中尊重对象生命周期 即使用goto跳出块将为适当的局部变量运行析构函数 start NonTrivial object if again goto start will call object NonTriv
  • 释放分配的内存

    这是好的做法吗 或者我应该替换之间的代码块 and 有一个功能 它可以重复使用 我承认 但我这样做的唯一动机是取消分配colsum因为它是huge并且不需要这样我就可以释放分配的内存 vector
  • 是否有比嵌套“使用”更好的确定性处置模式?

    在 C 中 如果我想确定性地清理非托管资源 我可以使用 using 关键字 但对于多个依赖对象 这最终会嵌套得越来越深 using FileStream fs new FileStream c file txt FileMode Open
  • 是否可以在不展开调用堆栈的情况下终止 Windows XP 上的 C++ 应用程序?

    我的理解是 当您在 Windows XP 中通过任务管理器终止 C 应用程序时 该应用程序仍然 干净 地被破坏 即调用堆栈将展开并且所有相关的对象析构函数将被调用 不知道我这里的理解是否有误 是否可以立即终止这样的应用程序而不展开堆栈 例如

随机推荐