应优先使用 std::make_unique 创建 unique_ptr,因其具备异常安全保证;不支持自定义删除器和带删除器的数组,需手写构造;所有权转移必须用 std::move,禁止拷贝。

直接用 std::make_unique 创建最安全
别手写 new 配 unique_ptr,容易漏掉异常安全处理。比如 unique_ptr 在构造参数里抛异常时,new 出来的内存就泄露了。std::make_unique 内部用完美转发+异常保护,一步到位:
auto p = std::make_unique<int>(100);
auto arr = std::make_unique<int>(10); // 数组版本
auto obj = std::make_unique<:string>("hello");</:string></int></int>
注意:make_unique 不支持自定义删除器(如 FILE*),这种得手写构造。
转移所有权只能用 std::move,不能赋值或拷贝
unique_ptr 禁止拷贝,所以 p2 = p1 或 func(p1)(按值传参)会编译失败,报错类似 use of deleted function。必须显式调用 std::move 表明“我要交出控制权”:
auto p1 = std::make_unique<int>(42); auto p2 = std::move(p1); // ✅ 转移成功,p1 变成空(get() == nullptr) // 此时再访问 *p1 会解引用空指针</int>
常见误操作:
- 忘了
std::move,以为赋值就行 - 转移后继续用原指针,没检查
p1是否为空 - 在函数返回时写
return p1;却没加std::move—— 其实这里编译器通常会自动移动(RVO/NRVO 优化),但显式写更清晰、更可靠
函数间传递 unique_ptr 的三种典型方式
取决于你想要什么语义:
- 想把所有权交给函数?用值参:
void consume(std::unique_ptr,调用方必须p) std::move(p) - 只想读/临时用,不改变归属?用
const std::unique_ptr或更推荐& T*/T&(避免不必要的智能指针开销) - 想让函数修改内部对象,但不拿走所有权?用
std::unique_ptr(可重置、可交换)&
例如工厂函数常返回 unique_ptr:auto create_thing() { return std::make_unique,接收方自然获得所有权。
数组和自定义删除器的创建要特别小心
普通 make_unique 支持数组,但不支持带删除器的数组;自定义删除器(比如关文件、释放 C 资源)必须手写构造:
// ✅ 正确:带自定义删除器的 FILE*
auto file = std::unique_ptr<file decltype>{fopen("log.txt", "w"), &fclose};
<p>// ❌ 错误:make_unique 不接受删除器参数
// auto bad = std::make_unique<file ...>; // 编译不过</file></p></file>
数组版 unique_ptr 的 [] 操作符可用,但不能用 .get() 返回的指针去 delete —— 必须用 delete[],而 unique_ptr 已内置该行为。漏掉 [] 模板参数会导致内存泄漏或崩溃。
转移所有权不是“复制”,是“移交钥匙”。一旦 std::move 出去,原变量就失效了,这点比裸指针还严格——编译器能帮你挡住很多误用,但前提是别绕过它。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











