C++资源管理手法之RAII类
引入
RAII 类作为一种资源管理的手段,为解决 C++ 内存泄漏而出现。
内存泄漏最常见的形式是用裸指针在堆上分配的内存空间(资源),之后忘了释放(简单地说就是 new 了后忘记 delete)。
int* p = new int[10];
// 使用p
// ......
// 忘了delete
// delete [] p;
即使记得写了 delete 释放资源,也可能因为在使用资源的过程中发生异常,进而 delete 语句不能执行,导致资源泄露。
RAII类介绍
RAII,Resource Acquisition Is Initialization,资源获取的时机就是资源初始化的时候。
RAII 类的主要思想是接管裸指针指向的资源,借助类对象在出作用域后会自动调用析构函数来回收资源。
class IntRaii
{
private:
int* _p;
public:
IntRaii(int *p): _p(p) {}
~IntRaii() {
delete [] _p;
}
};
void test() {
int* p = new int[10]; // 资源初始化的时候
IntRaii ir(p); // 就是资源接管的时机
// IntRaii ir(new int[10]) // p是多余的
// 使用资源......
// 无论怎么样ir是个局部类对象
// 其生存周期结束时 (比如函数执行完毕) 自动调用析构函数销毁
// 其管理的资源也得到了释放
}
RAII 的主要思想就是这么多,但是还有更多的细节,最常见的细节讨论是关于 RAII 对象是否支持复制。
- 禁止复制。在类中将 copy 操作设置为 private (传统手法)或 delete 掉(C++11之后)。例如互斥锁的 RAII 对象(表示使用 RAII 手法管理互斥锁)和 unique_ptr 对象一般来说不允许复制;
- 允许复制。在类中仔细地实现所有 copy 操作,进行深拷贝,产生完完整整、互不影响的副本。
- 对底层资源使用引用计数。只进行
- 浅拷贝,拷贝指向资源的指针而不拷贝资源,如此一来就有多个对象引用同一份底层资源,例如 shared_ptr 对象。
- 转移底层资源的所有权。这种资源的特征是一份底层资源只允许有一个管理者(只允许有一个指针指向它),对这样的对象进行 copy 时需要将原管理者的指针置位空。
其实 RAII 对象是否允许复制完全取决于特定的应用场景,随机应变,自行仔细小心手撸 ctor 和 dtor 即可。
RAII类的实践
- 最典型实践的就是 C++11 引入的智能指针。
unique_ptr
,shared_ptr
和weak_ptr
,它们都是 RAII 类模板(过渡期有个auto_ptr
,不过已经废弃了)。 - 常见的实践还有
SmartLock
,这种类对象接管一个互斥锁时就调用lock
方法,销毁时由析构函数调用unlock
方法,避免忘了解锁而导致死锁。