c++如何使用智能指针_c++ unique_ptr与shared_ptr内存管理【详解】

结论:用 std::unique_ptr 管理独占资源,用 std::shared_ptr 管理共享资源;必须用 make_unique/make_shared 创建,禁止裸 new;unique_ptr 可移转为 shared_ptr,反之不行;注意循环引用、所有权边界和数组删除器。

直接说结论:用 std::unique_ptr 管理独占资源,用 std::shared_ptr 管理共享资源;别混用裸指针和智能指针初始化,也别用 new 直接构造后赋值给 shared_ptr —— 容易 double-delete 或内存泄漏。

什么时候该用 unique_ptr

当你明确知道某块内存只属于一个所有者,且生命周期由当前作用域或对象严格控制时。典型场景包括:函数返回动态对象、容器里存指针、RAII 封装资源(如文件句柄、socket)。

  • 必须用 std::make_unique() 创建,避免裸 new
    auto ptr = std::make_unique(42); // ✅ 推荐
    auto ptr2 = std::unique_ptr(new int(42)); // ❌ 不安全,异常可能泄漏
  • 不能拷贝,只能移动:
    auto a = std::make_unique(10);
    auto b = std::move(a); // ✅ a 变为空
    // auto c = a; // ❌ 编译失败
  • 析构自动调用 delete,不需手动 reset() —— 除非你想提前释放

什么时候该用 shared_ptr

当多个对象/模块需要共同持有同一块内存,并且谁都不想“强制决定”销毁时机时。常见于观察者模式、缓存、异步回调中跨线程传递数据。

  • 必须用 std::make_shared() 构造:
    auto sp = std::make_shared("hello"); // ✅ 一次分配,高效
    auto sp2 = std::shared_ptr(new std::string("hello")); // ❌ 两次分配,且异常不安全
  • 引用计数在拷贝/赋值时 +1,离开作用域或 reset() 时 -1;计数归零才真正释放内存
  • 注意循环引用:两个 shared_ptr 互相持有对方,会导致永远不释放 —— 此时要用 std::weak_ptr 打断环

unique_ptr

shared_ptr 能相互转换吗?

可以,但有严格限制:

  • unique_ptrshared_ptr:允许(因为所有权可转移),用移动语义
    auto up = std::make_unique(100);
    std::shared_ptr sp = std::move(up); // ✅ up 为空,sp 拿到所有权
  • shared_ptrunique_ptr:不允许(语义冲突)。你不能把“共享所有权”强行变成“独占”,编译器会拒绝
  • 都转成裸指针(.get())再传参?可以,但立刻失去自动管理能力 —— 这是退化行为,仅限对接 C API 或 legacy 代码

最容易被忽略的坑

不是语法写错,而是对“所有权边界”没想清楚:

  • shared_ptr 存进容器(如 std::vector)后,误以为清空容器就释放了内存 —— 实际上只要还有别的 shared_ptr 持有它,就不会释放
  • 在类成员里用 shared_ptr 持有自身(比如回调里捕获 shared_from_this()),又没检查 this 是否已失效,导致访问已析构对象
  • unique_ptr 管理数组?必须显式指定删除器:
    auto arr = std::unique_ptr(new int[10]); // ✅
    // auto bad = std::unique_ptr(new int[10]); // ❌ 用 delete 而非 delete[]