RAII
资源获取即初始化 或 RAII,是一种 C++ 编程技术[1][2],它将必须在使用前获取的资源(分配的堆内存、执行线程、打开的套接字、打开的文件、锁定的互斥锁、磁盘空间、数据库连接——任何供应有限的事物)的生命周期绑定到对象的生命周期。
RAII 保证资源对任何可能访问该对象的函数可用(资源可用性是类不变量,消除了冗余的运行时测试)。它还保证所有资源在控制对象的生命周期结束时被释放,释放顺序与获取顺序相反。同样,如果资源获取失败(构造函数抛出异常退出),则每个完全构造的成员和基类子对象所获取的所有资源都将按照初始化的相反顺序释放。这利用了核心语言特性(对象生命周期、作用域退出、初始化顺序和堆栈展开)来消除资源泄漏并保证异常安全性。此技术的另一个名称是作用域绑定资源管理 (SBRM),以 RAII 对象的生命周期因作用域退出而结束的基本用例命名。
RAII 可以总结如下
- 将每个资源封装到一个类中,其中
- 构造函数获取资源并建立所有类不变量,或者在无法完成时抛出异常,
- 析构函数释放资源,并且永远不会抛出异常;
- 始终通过 RAII 类的实例使用资源,该实例
- 本身具有自动存储持续时间或临时生命周期,或者
- 其生命周期受自动或临时对象的生命周期限制。
移动语义支持在对象之间、容器内外以及跨线程传输资源和所有权,同时确保资源安全。 |
(自 C++11 起) |
具有 open()
/close()
、lock()
/unlock()
或 init()
/copyFrom()
/destroy()
成员函数的类是非 RAII 类的典型示例
std::mutex m; void bad() { m.lock(); // acquire the mutex f(); // if f() throws an exception, the mutex is never released if (!everything_ok()) return; // early return, the mutex is never released m.unlock(); // if bad() reaches this statement, the mutex is released } void good() { std::lock_guard<std::mutex> lk(m); // RAII class: mutex acquisition is initialization f(); // if f() throws an exception, the mutex is released if (!everything_ok()) return; // early return, the mutex is released } // if good() returns normally, the mutex is released
[编辑] 标准库
管理自身资源的 C++ 库类遵循 RAII:std::string、std::vector、 std::jthread(自 C++20 起) 以及许多其他类在其构造函数中获取资源(在错误时抛出异常),在其析构函数中释放资源(从不抛出异常),并且不需要显式清理。
此外,标准库提供了几个 RAII 包装器来管理用户提供的资源
|
(自 C++11 起) |
[编辑] 注释
RAII 不适用于未使用前未获取的资源的管理:CPU 时间、核心可用性、缓存容量、熵池容量、网络带宽、电力消耗、堆栈内存。对于此类资源,C++ 类构造函数无法保证对象生命周期内的资源可用性,并且必须使用其他资源管理方法。