3Effective C++(第3版)

资源管理

资源即对象

智能指针new/delete配对克隆资源泄漏

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第3章:资源管理(Resource Management)

一、导读

1.1 本章学习目标

第3章讲解 C++ 中资源管理的核心原则:RAII(Resource Acquisition Is Initialization)。通过对象的生命周期管理资源,确保资源在对象销毁时自动释放。

本章核心学习目标:

  • 掌握 RAII 的核心思想:资源获取即初始化
  • 理解智能指针的使用:unique_ptr、shared_ptr、weak_ptr
  • 掌握资源泄漏的预防方法:如何确保 new/delete 配对
  • 理解克隆(clone)方法的使用场景

1.2 为什么本章如此重要

资源泄漏是 C++ 中最常见也最危险的错误之一。RAII 和智能指针是解决这一问题的核心工具。

1.3 核心问题引导

RAII 如何保证异常安全?

unique_ptr 和 shared_ptr 的选择标准是什么?

为什么应该用智能指针而非裸指针?


二、核心概念详解

2.1 RAII 原则

cpp
// 不好: 手动管理资源
void processFile() {
    FILE* fp = fopen("data.txt", "r");
    if (!fp) return;
    
    char* buffer = new char[1024];
    // ... 处理文件 ...
    if (some_error) return;  // 忘记释放 buffer!
    
    delete[] buffer;
    fclose(fp);
}

// 好: RAII 自动管理
void processFileRAII() {
    std::ifstream file("data.txt");  // 构造时打开
    if (!file) return;
    
    std::vector<char> buffer(1024);  // 自动管理内存
    // ... 处理文件 ...
    if (some_error) return;  // 自动清理!
}  // file 和 buffer 自动析构

// RAII 的核心: 资源的生命周期 = 对象的生命周期
// 构造获取资源,析构释放资源

2.2 智能指针

cpp
// unique_ptr: 独占所有权
{
    auto ptr = std::make_unique<Widget>(42);  // C++14
    ptr->doSomething();
}  // 自动 delete

// shared_ptr: 共享所有权,引用计数
{
    auto sp1 = std::make_shared<Widget>(42);
    {
        auto sp2 = sp1;  // 引用计数 = 2
    }  // sp2 析构,引用计数 = 1
}  // sp1 析构,引用计数 = 0,delete Widget

// weak_ptr: 不增加引用计数,解决循环引用
class Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;  // 弱引用,不阻止析构
};

// 使用场景:
// unique_ptr: 工厂函数返回值、独占资源
// shared_ptr: 共享缓存、观察者模式
// weak_ptr: 缓存、打破循环引用

2.3 防止资源泄漏

cpp
// 不好: 容易忘记释放
Widget* createWidget() {
    return new Widget(42);
}
void useWidget() {
    Widget* w = createWidget();
    w->doSomething();
    delete w;  // 容易忘记
}

// 好: 返回智能指针
std::unique_ptr<Widget> createWidgetSafe() {
    return std::make_unique<Widget>(42);
}
void useWidgetSafe() {
    auto w = createWidgetSafe();
    w->doSomething();
}  // 自动释放

三、实战案例

3.1 案例一:自定义 RAII 包装器

cpp
// 用 unique_ptr 管理 C 风格资源
struct FileCloser {
    void operator()(FILE* fp) const {
        if (fp) fclose(fp);
    }
};

using UniqueFile = std::unique_ptr<FILE, FileCloser>;

UniqueFile openFile(const char* path, const char* mode) {
    return UniqueFile(fopen(path, mode));
}

void processFile() {
    auto file = openFile("data.txt", "r");
    if (!file) throw std::runtime_error("Cannot open file");
    
    char buffer[1024];
    size_t n = fread(buffer, 1, sizeof(buffer), file.get());
    // ... 处理数据 ...
}  // file 自动关闭

// 互斥锁的 RAII 包装
class MutexGuard {
    std::mutex& mtx;
public:
    explicit MutexGuard(std::mutex& m) : mtx(m) { mtx.lock(); }
    ~MutexGuard() { mtx.unlock(); }
    MutexGuard(const MutexGuard&) = delete;
    MutexGuard& operator=(const MutexGuard&) = delete;
};

void threadSafeFunction() {
    static std::mutex mtx;
    MutexGuard guard(mtx);  // 构造时加锁
    // ... 临界区 ...
}  // 析构时自动解锁

四、本章小结

RAII 通过对象生命周期管理资源,是 C++ 资源管理的核心原则

unique_ptr 用于独占所有权,shared_ptr 用于共享所有权

weak_ptr 用于打破循环引用,不增加引用计数

优先使用智能指针而非裸指针,避免资源泄漏


五、思考与练习

思考题

shared_ptr 的引用计数为什么需要原子操作?

为什么工厂函数应该返回 unique_ptr 而非裸指针?

编程练习

实现一个 RAII 的数据库连接包装器。

实现一个简单的 shared_ptr(包含引用计数)。


3/9资源管理
0%