unique_ptr不可拷贝只能移动,需用std::move转移所有权;推荐std::make_unique创建,避免异常安全问题;函数返回时自动移动,无需显式std::move;存入容器时确保指向堆对象,跨线程等场景须明确所有权边界。

unique_ptr 不能拷贝,只能移动
这是最常踩的坑:直接写 std::unique_ptr<int> p2 = p1;</int> 会编译失败,报错信息类似 use of deleted function 'std::unique_ptr<_tp _dp>::unique_ptr(const std::unique_ptr<_tp _dp>&)'</_tp></_tp>。因为 unique_ptr 的拷贝构造函数被显式删除了,它只允许转移所有权。
正确做法是用 std::move 显式移交控制权:
std::unique_ptr<int> p1 = std::make_unique<int>(42); std::unique_ptr<int> p2 = std::move(p1); // p1 现在为空,p2 持有对象
- 移动后原指针变为空(
p1 == nullptr成立) - 如果忘了
std::move,编译器会直接拒绝,不是运行时崩溃——这是安全设计,但新手容易卡在这一步 - 函数传参/返回时也一样:想把所有权交出去,参数类型得是
std::unique_ptr<t></t>或std::unique_ptr<t>&&</t>,不能是const std::unique_ptr<t>&</t>(后者只能观察,不能移出)
make_unique 是首选创建方式
别手写 new 配合构造函数,比如 std::unique_ptr<int>(new int(42))</int>。这看似可行,但有异常安全风险:如果后续构造其他对象抛异常,new 出来的内存可能泄漏。
std::make_unique 能保证“全成功或全不发生”:
rebuild是一款高度可配置化的企业管理系统!可免费商用!低代码/零代码快速搭建企业中台、OA办公自动化、CRM客户关系管理、WMS库存管理、TMS运输管理、SCM供应链管理,甚至是 ERP 企业资源计划!REBUILD 侧重于业务需求实现,而非基础技术框架或项目启动模板,通过 REBUILD 可以真正实现零代码快速搭建,无需编程、无需编译代码,甚至无需了解技术。 使用开始使用 REBUIL
立即学习“C++免费学习笔记(深入)”;
auto p = std::make_unique<std::string>("hello"); // 安全
auto q = std::make_unique<int[]>(100); // 创建数组,注意括号是 () 不是 []
- 不支持自定义删除器的直接初始化(如文件句柄),这时才需要手写
unique_ptr构造:std::unique_ptr<file decltype> fp(fopen("x.txt", "r"), &fclose);</file> - 数组版本(
int[])和单对象版本(int)的unique_ptr类型不兼容,删除器也不同,混用会编译失败 - C++14 才引入
make_unique,老项目若限 C++11,得自己封装或接受裸 new 的风险
作为函数返回值时,移动语义自动触发
很多人担心“返回 unique_ptr 会不会触发拷贝”,其实完全不必:C++11 起,满足条件的局部对象返回会被强制移动(即使没写 std::move),编译器还会做返回值优化(RVO)进一步省掉移动开销。
std::unique_ptr<std::vector<double>> load_data() {
auto ptr = std::make_unique<std::vector<double>>();
ptr->resize(1000000);
return ptr; // 这里自动移动,无需 std::move(ptr)
}
- 返回右值引用(如
return std::move(ptr))反而可能阻止 RVO,纯属多此一举 - 如果函数参数是
unique_ptr值类型,调用方必须移动传入:process(load_data());合法,但process(p);(p 是左值)需写成process(std::move(p)); - 返回
nullptr是合法且常见的空状态表达,比返回裸指针更明确意图
和容器一起用要注意所有权边界
把 unique_ptr 存进 std::vector 很常见,但容易误以为“容器销毁时自动释放所有对象”就万事大吉——其实只是释放了指针本身,真正资源是否释放,取决于你有没有让每个 unique_ptr 指向有效堆对象。
std::vector<std::unique_ptr<int>> v; v.push_back(std::make_unique<int>(1)); v.push_back(std::make_unique<int>(2)); // v 析构时,两个 int 自动 delete
- 别往容器里塞从栈上取地址的指针(如
int x = 42; v.push_back(std::unique_ptr<int>(&x));</int>),析构时会 double-delete 或访问非法内存 - 容器元素之间互不影响所有权,
v[0].reset()只释放第一个对象,不影响其余 - 如果需要共享所有权,
unique_ptr不合适,该换std::shared_ptr;但共享有引用计数开销,别为偷懒滥用
真正难的不是语法,是判断“谁该拥有这块内存”。一旦跨线程、进回调、塞进第三方库接口,unique_ptr 的移动边界就很容易模糊——这时候宁可多写几行注释,也别靠直觉猜所有权归属。








