17370845950

C++中的unique_ptr怎么用?(实现独占式资源管理)
unique_ptr 不能复制只能移动,出生即绑定资源、析构自动释放;最安全创建方式是 std::make_unique,支持数组和自定义删除器,访问前需判空,release/reset 控制释放,误用易致未定义行为。

unique_ptr 不能复制,只能移动;它一出生就绑定资源,析构时自动释放——这是它实现独占式资源管理的核心机制。

怎么创建和初始化 unique_ptr

最安全的方式是用 std::make_unique,它避免裸 new 可能引发的异常安全问题(比如构造参数中抛异常导致内存泄漏)。

  • 正确:
    auto ptr = std::make_unique(42);
  • 不推荐:
    std::unique_ptr ptr(new int(42));
    (裸 new 容易出错,且无法传递数组长度)
  • 数组支持:
    auto arr = std::make_unique(10); // 注意 [] 语法
    ,此时调用的是 delete[]
  • 自定义删除器必须在构造时指定,不能后期修改:
    std::unique_ptr ptr(new int(42), [](int* p) { delete p; });

为什么不能拷贝,只能移动

拷贝语义会破坏“独占”前提:两个指针同时指向同一块内存,谁析构谁负责?unique_ptr 的拷贝构造函数和 operator= 都被显式 delete 了。

  • 错误写法:
    auto p1 = std::make_unique(1);
    auto p2 = p1; // 编译失败:use of deleted function
  • 正确转移所有权:
    auto p2 = std::move(p1); // p1 变成 nullptr,p2 持有资源
  • 函数传参/返回也必须用移动:
    void take(std::unique_ptr p) { /* p 接收所有权 */ }
    auto p = std::make_unique(1);
    take(std::move(p)); // p 此后为空

如何安全访问和释放资源

直接解引用前务必检查是否为空,否则触发未定义行为;显式释放用 release(),但要自己承担后续释放责任。

  • 访问:ptr.get() 返回原始指针(只读),*ptrptr->member 解引用(需确保非空)
  • 判空:if (ptr)if (ptr != nullptr),别用 if (ptr.get()) 多此一举
  • 手动交出控制权:
    int* raw = ptr.release(); // ptr 变成 nullptr,raw 必须手动 delete
  • 重置资源:ptr.reset(new int(99)) 会先释放旧资源再接管新资源;ptr.reset() 等价于 ptr = nullptr

常见误用和陷阱

最容易被忽略的是数组类型和自定义删除器的匹配问题——类型系统不会帮你校验删除器逻辑是否正确。

  • make_unique 创建数组,却用普通 unique_ptr 接收 → 编译报错(类型不匹配)
  • 手动 new 数组,却用默认删除器的 unique_ptr 管理 → delete 而非 delete[],UB
  • unique_ptr 存入容器(如 std::vector<:unique_ptr>>)没问题,但存进 std::set 或作为 std::map key 就不行——因为不可比较、不可拷贝
  • 跨 DLL 边界传递 unique_ptr(尤其带自定义删除器)可能因 ABI 不一致崩溃,优先用裸指针 + 明确约定生命周期

真正难的不是语法,而是判断“该不该用 unique_ptr”:如果资源需要共享或延迟释放,它就不是解;如果对象生命周期本就由栈管理,加一层 unique_ptr 反而增加间接性。它的价值只在“我确定只有我管这块内存,且我走的时候必须清掉”。