C++资源管理手法之RAII类

发布时间 2023-04-20 15:16:05作者: zwjason

引入

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 对象是否支持复制。

  1. 禁止复制。在类中将 copy 操作设置为 private (传统手法)或 delete 掉(C++11之后)。例如互斥锁的 RAII 对象(表示使用 RAII 手法管理互斥锁)和 unique_ptr 对象一般来说不允许复制;
  2. 允许复制。在类中仔细地实现所有 copy 操作,进行深拷贝,产生完完整整、互不影响的副本。
  3. 对底层资源使用引用计数。只进行
  4. 浅拷贝,拷贝指向资源的指针而不拷贝资源,如此一来就有多个对象引用同一份底层资源,例如 shared_ptr 对象。
  5. 转移底层资源的所有权。这种资源的特征是一份底层资源只允许有一个管理者(只允许有一个指针指向它),对这样的对象进行 copy 时需要将原管理者的指针置位空。

其实 RAII 对象是否允许复制完全取决于特定的应用场景,随机应变,自行仔细小心手撸 ctor 和 dtor 即可。

RAII类的实践

  1. 最典型实践的就是 C++11 引入的智能指针。unique_ptrshared_ptrweak_ptr,它们都是 RAII 类模板(过渡期有个auto_ptr,不过已经废弃了)。
  2. 常见的实践还有 SmartLock,这种类对象接管一个互斥锁时就调用 lock 方法,销毁时由析构函数调用 unlock 方法,避免忘了解锁而导致死锁。