三、智能指针:自动管理动态内存,避免内存泄漏

痛点场景:

C++中用new动态分配内存后,必须用delete手动释放,否则会导致内存泄漏。实际开发中,很容易因忘记delete、异常导致delete未执行、或重复delete而崩溃。

什么是智能指针?

智能指针是封装了原始指针的类,它通过RAII(资源获取即初始化) 机制,在智能指针的生命周期结束时(如离开作用域),自动调用delete释放内存。
C++标准库提供了三种常用智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptr

1. std::unique_ptr:独占所有权

  • 特性:一个对象只能被一个unique_ptr拥有,所有权不可复制(只能移动)。
  • 用途:管理“独占”的动态资源(如局部动态对象、类的成员对象)。

示例:避免手动delete

场景:管理类的成员资源
比如一个File类,需要管理文件指针(动态资源):

class File {
private:
    // 用unique_ptr管理FILE*(需自定义删除器,因为FILE*用fclose释放)
    std::unique_ptr<FILE, decltype(&fclose)> fp; 
public:
    File(const char* filename, const char* mode) 
        : fp(fopen(filename, mode), &fclose) { // 构造时打开文件
        if (!fp) {
            throw std::runtime_error("文件打开失败");
        }
    }

    // 无需写析构函数!fp离开作用域时自动调用fclose关闭文件
};

int main() {
    try {
        File file("test.txt", "w"); // 打开文件
        // 使用文件...
    } catch (...) {
        // 即使发生异常,file的fp也会自动关闭,不会泄漏文件句柄
    }
    return 0;
}

2. std::shared_ptr:共享所有权

  • 特性:多个shared_ptr可以共享同一个对象,内部通过“引用计数”记录所有者数量,当最后一个shared_ptr销毁时,才释放对象。
  • 用途:管理“共享”的动态资源(如多个对象需要引用同一个资源)。

示例:多对象共享资源

// 共享的资源类
class Resource {
public:
    Resource() { std::cout << "Resource创建\n"; }
    ~Resource() { std::cout << "Resource销毁\n"; }
};

void func1(std::shared_ptr<Resource> ptr) {
    std::cout << "func1中引用计数:" << ptr.use_count() << "\n"; // 2
}

int main() {
    // 创建shared_ptr,引用计数=1
    std::shared_ptr<Resource> ptr = std::make_shared<Resource>(); 
    std::cout << "main中引用计数:" << ptr.use_count() << "\n"; // 1

    func1(ptr); // 传递给func1,引用计数=2(func1的ptr和main的ptr共享)

    std::cout << "func1结束后引用计数:" << ptr.use_count() << "\n"; // 1

    // main结束,ptr销毁,引用计数=0,Resource被自动销毁
    return 0;
}

输出

Resource创建
main中引用计数:1
func1中引用计数:2
func1结束后引用计数:1
Resource销毁

3. std::weak_ptr:解决shared_ptr的循环引用

  • 痛点:如果两个shared_ptr互相引用,会导致引用计数永远不为0,资源无法释放(循环引用泄漏)。
  • 特性weak_ptrshared_ptr的“弱引用”,不增加引用计数,可用于观察shared_ptr管理的对象,不影响其生命周期。

用weak_ptr解决

class B;
class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A销毁\n"; }
};
class B {
public:
    std::weak_ptr<A> a_ptr; // 用weak_ptr引用A,不增加计数
    ~B() { std::cout << "B销毁\n"; }
};

int main() {
    {
        auto a = std::make_shared<A>();
        auto b = std::make_shared<B>();
        a->b_ptr = b; // b计数=2
        b->a_ptr = a; // a计数仍为1(weak_ptr不增加计数)
    } 
    // a离开作用域,计数减为0 → A销毁 → a->b_ptr销毁,b计数减为1
    // b离开作用域,计数减为0 → B销毁
    // 输出:A销毁  B销毁(无泄漏)
    return 0;
}

智能指针的核心意义:

  • 自动管理内存:避免手动delete,从根源上减少内存泄漏。
  • 安全:即使发生异常,也能保证资源释放(RAII机制)。
  • 明确所有权unique_ptr(独占)和shared_ptr(共享)清晰表达资源的使用方式,代码更易理解。

总结:这些特性解决了什么问题?

特性 解决的痛点 核心价值
回调 组件无法预知未来要执行的逻辑 解耦组件与业务,提高灵活性
std::function 各种可调用对象接口不统一,难以管理 统一接口,简化可调用对象的存储和传递
智能指针 手动管理内存易泄漏、崩溃 自动释放资源,提高代码安全性和可维护性

理解它们的关键是:这些特性都是为了让代码更符合实际开发需求——更灵活、更安全、更少出错。在复杂项目中,不用这些特性会导致代码臃肿、易错,而用好它们能显著提升开发效率和代码质量。