C++里的智能指针unique_ptr有什么特点?(独占所有权与不可拷贝)

12次阅读

unique_ptr不能拷贝,因其构造函数赋值运算符被显式删除,以确保资源独占;拷贝会导致双重释放,故仅支持通过std::move转移所有权,移动后原指针为空。

C++里的智能指针unique_ptr有什么特点?(独占所有权与不可拷贝)

unique_ptr 为什么不能拷贝?

因为 unique_ptr 的设计目标就是「独占资源」,它的构造函数和赋值运算符都被显式删除了(= delete)。一旦允许拷贝,两个指针就可能同时指向同一块内存,析构时触发双重释放——这是未定义行为,程序大概率崩溃。

常见错误现象:

  • unique_ptr p1 = make_unique(42); unique_ptr p2 = p1; → 编译失败,报错类似 use of deleted function 'std::unique_ptr<_tp _dp>::unique_ptr(const std::unique_ptr<_tp _dp>&)'
  • 传参时写 void func(unique_ptr p) 然后调用 func(p1) → 同样编译失败,除非你主动用 std::move(p1)

移动语义是唯一合法的“转移”方式

所有权只能通过移动(std::move)转移,移动后原指针变为空(get() == nullptr),新指针接管资源。这符合「独占」的语义约束,也避免了运行时开销。

实操要点:

立即学习C++免费学习笔记(深入)”;

  • 移动后原 unique_ptr 不再持有对象,再次解引用(如 *p1)会触发空指针解引用,行为未定义
  • 函数返回 unique_ptr 时,现代编译器通常启用 RVO 或移动优化,无需手动 std::move;但函数参数接收时必须显式 std::move
  • 容器中存储 unique_ptr(如 vector>)是安全且高效的,插入/删除都靠移动完成
unique_ptr create_int() {     return make_unique(100); // OK:返回值自动移动 } unique_ptr p1 = create_int(); // OK 

unique_ptr p2 = std::move(p1); // OK:显式转移 // cout << p1; // 危险!p1 已为空 cout << p2; // 输出 100

和 raw pointer、shared_ptr 的关键区别在哪?

对比不是为了教你怎么选,而是帮你判断「是不是真该用 unique_ptr」:

  • vs raw pointer:unique_ptr 自动析构,不用手写 delete;但不能隐式转成 T*,需调用 .get()(仅获取地址,不移交所有权)
  • vs shared_ptrunique_ptr 零开销(无引用计数)、无线程同步成本;但无法共享所有权——如果多个模块需要同时访问同一对象,它就不合适
  • 适用场景典型包括:函数内部临时资源管理、PIMPL 惯用法、工厂函数返回、STL 容器元素(替代裸指针数组)

容易被忽略的细节:自定义删除器与数组特化

默认情况下 unique_ptrdelete,但若你管理的是数组(new T[10])或 C 风格资源(如 fopen 文件句柄),必须显式指定删除器,否则行为未定义。

  • 数组用法必须写成 unique_ptr,否则 delete 会调用错的析构路径
  • 自定义删除器会改变类型: unique_ptrunique_ptr 是不同类型,不能互相赋值(即使都用 std::move
  • 删除器对象本身会被存储在 unique_ptr 实例里,若删除器是函数指针(轻量),几乎无额外开销;若为带状态的 Lambda,则需注意大小
// 正确的数组用法 unique_ptr arr(new int[5]{1,2,3,4,5}); 

// 正确的 FILE 管理 auto del_file = [](FILE f) { if (f) fclose(f); }; unique_ptr fp(fopen("a.txt", "r"), del_file);

真正用好 unique_ptr 的难点不在语法,而在于时刻意识到:它代表一种明确的所有权契约——谁 move 谁负责,move 后原变量即失效,没有商量余地。

text=ZqhQzanResources