聊聊 C++ RAII
1. RAII
代码中常常需要管理资源,可能是内存,可能是锁,或者是某个 handle 等等,下面抽象了 acquire, release 两个接口分别表示获取和释放资源。
1.1. 问题场景
1 | void *acquire(int size) { |
会有如下打印
1 | acquire 100 |
上面是 C 风格实现,这种实现需要程序员保证获取和释放一一对应,所以常常导致资源没释放,或者重复释放的问题。
RAII 意思是在 scope 内申请的资源在退出 scope 的时候自动释放,不用程序员来管理。从而极大地解放了程序员的负担,也从根本上杜绝资源的泄漏和重复释放。
2. 使用 smart ptr
C++ built-in 的 smart ptr 具备 RAII 能力。
2.1. 指定 deleter
这样可以用 unique_ptr 的 RAII 机制,当 doSomething() 结束时,会调用 deleter 函数,也就是 release。
实际上 acquire,release 是对 malloc,free 的封装,如果就用 malloc,free 的话是不需要特别指定的。
1 | void doSomething() { |
但是这种做法有一种情况似乎不好处理,当 acquire 定义如下,就比较麻烦了。
1 | void acquire2(void **ptr, int size) |
对于上面处理不了的情形,可以用下面的办法,可以用。
1 | void doSomething() { |
这种实现不推荐,有以下两个原因
- 长得丑;
- ptr 还在,很可能被拿来做别的事情,也可能被 double free;
2.2. 使用 make_unique()
对需要管理的资源封装成一个资源管理类,在 constructor, destructor 中进行申请和释放。
make_unique是C++14引入的函数,它提供了一个更简单安全的方式来创建std::unique_ptr。
std::make_unique可以避免手动使用new来创建对象,它将对象创建和内存分配合并到一步,这样可以避免手动delete带来的潜在危险。
1 | class CustomMemoryMan { |
3. Summary
推荐最后的版本,即封装一个资源管理类,再使用 make_unique 来构建。