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++ 类构造函数无法保证资源在对象生命期内的可用性,必须使用其他资源管理方法。