深入理解C++中的RAII与智能指针

在C++中,资源获取即初始化(RAII)是管理资源的核心思想。通过在对象的构造函数中获取资源,在析构函数中释放资源,能够保证异常安全、内存泄漏最小化。C++11 引入了三大智能指针(std::unique_ptrstd::shared_ptrstd::weak_ptr),它们正是基于 RAII 实现的。

1. std::unique_ptr

  • 独占所有权:同一时刻只能有一个 unique_ptr 拥有资源。
  • 无复制:默认不可拷贝,只有移动语义。
  • 自定义删除器:可以为非 new 分配的资源(如 malloc、文件句柄)提供删除器。
  • 使用场景:局部资源管理、返回对象时转移所有权。
std::unique_ptr<int[]> arr(new int[10]);   // 动态数组
arr[0] = 42;

2. std::shared_ptr

  • 共享所有权:多处引用同一资源,引用计数机制确保最后一个指针销毁时释放。
  • 拷贝语义:复制 shared_ptr 会共享计数。
  • 循环引用:如果互相持有 shared_ptr,会导致内存泄漏,需要 std::weak_ptr 断开循环。
  • 使用场景:需要共享所有权、实现引用计数对象。
std::shared_ptr <Foo> p1 = std::make_shared<Foo>();
std::shared_ptr <Foo> p2 = p1;   // 计数+1

3. std::weak_ptr

  • 非拥有指针:仅观察对象,不影响引用计数。
  • 防止循环引用:常与 shared_ptr 配合使用。
  • 锁定:使用 lock() 获取临时 shared_ptr,若对象已销毁返回空指针。
std::weak_ptr <Foo> wp = p1;   // 观察
if (auto sp = wp.lock()) {
    // 对象存活
}

4. 自定义删除器

在智能指针中,我们可以为资源指定自定义删除器,适配各种资源类型。

struct FileCloser {
    void operator()(FILE* f) const { fclose(f); }
};

std::unique_ptr<FILE, FileCloser> file(fopen("data.txt", "r"));

5. RAII 与异常安全

RAII 的核心优势在于异常安全。假设在函数内部分配了多个资源,若某一步抛异常,已经分配的资源会自动在局部对象析构时释放,避免泄漏。

void func() {
    std::unique_ptr <int> p1(new int(10));
    std::unique_ptr<int[]> p2(new int[5]);  // 可能抛异常
    // ...
}

6. 总结

  • RAII:资源绑定对象生命周期,提供异常安全。
  • unique_ptr:独占所有权,适合局部对象和移动语义。
  • shared_ptr:共享所有权,适合需要多处持有的对象。
  • weak_ptr:观察者,解决循环引用。
  • 自定义删除器:兼容多种资源。

通过合理使用 RAII 与智能指针,C++ 开发者能够编写更安全、更易维护的代码,避免手动管理内存和资源导致的错误。

发表评论