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

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_ptrcreate_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_ptr:unique_ptr零开销(无引用计数)、无线程同步成本;但无法共享所有权——如果多个模块需要同时访问同一对象,它就不合适 - 适用场景典型包括:函数内部临时资源管理、PIMPL 惯用法、工厂函数返回、STL 容器元素(替代裸指针数组)
容易被忽略的细节:自定义删除器与数组特化
默认情况下 unique_ptr 用 delete,但若你管理的是数组(new T[10])或 C 风格资源(如 fopen 文件句柄),必须显式指定删除器,否则行为未定义。
- 数组用法必须写成
unique_ptr,否则delete会调用错的析构路径 - 自定义删除器会改变类型:
unique_ptr和unique_ptr是不同类型,不能互相赋值(即使都用std::move) - 删除器对象本身会被存储在
unique_ptr实例里,若删除器是函数指针(轻量),几乎无额外开销;若为带状态的 lambda,则需注意大小
// 正确的数组用法 unique_ptrarr(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 后原变量即失效,没有商量余地。










