unique_ptr 初始化可为空或用make_unique/new,仅支持移动赋值不可拷贝;析构自动释放资源,数组需自定义删除器;推荐make_unique和无状态删除器,避免release和捕获lambda。

unique_ptr 初始化时必须指定所管理的对象
直接声明一个未初始化的 unique_ptr 是合法的,但它不指向任何对象;一旦想让它接管资源,就必须用 new、make_unique 或移动语义赋值。不能像原始指针那样先声明再赋值(除非用移动)。
std::unique_ptrp1; // 合法,p1为空 std::unique_ptrp2(new int(42)); // 合法,但不推荐——异常不安全 auto p3 = std::make_unique(42); // 推荐:异常安全、简洁 p1 = std::move(p3); // 只能靠 move 赋值,p3 变为空
不能拷贝 unique_ptr,只能移动
拷贝构造或拷贝赋值会编译失败,因为 unique_ptr 的拷贝构造函数和 operator= 被显式删除了。这是它“独占所有权”的核心约束,不是 bug,是设计意图。
- 错误写法:
std::unique_ptrp1 = std::make_unique (1); auto p2 = p1; // 编译错误 - 正确写法:
auto p2 = std::move(p1); // p1 现在为空,p2 持有原对象 - 传参时也一样:函数参数若需接收所有权,必须声明为
std::unique_ptr或直接按值传递(隐含 move)&&
释放资源的方式只有“离开作用域”或“主动 reset”
unique_ptr 的析构函数会自动调用其内部存储的删除器(默认是 delete),所以你几乎不需要手动干预内存释放。但要注意:它不会自动释放数组,除非你显式指定删除器。
- 普通对象:用
std::make_unique即可,析构调用() delete - 数组对象:必须用
std::unique_ptr+new int[10],否则delete会错用为delete[]—— 行为未定义 -
p.reset();等价于p = nullptr;,会立即释放当前所指对象(如果非空) - 调用
release()会交出裸指针控制权,之后unique_ptr不再负责释放——容易忘掉 delete,慎用
自定义删除器会让 unique_ptr 失去“零开销抽象”优势
默认情况下 unique_ptr 的删除器是空类型(std::default_delete),编译器通常能完全内联,无额外空间或运行时开销。但一旦传入函数对象或 lambda(尤其是捕获型),就可能引入函数指针存储或堆分配。
- 安全轻量的自定义删除器:
struct FreeDeleter { void operator()(void* p) const { std::free(p); } }; std::unique_ptrp(static_cast (std::malloc(100))); - 避免捕获 lambda:
[&](int* p) { log("deleting"); delete p; }会导致unique_ptr对象变大,且无法作为模板参数推导(需显式写全类型) - 多数场景下,优先用
make_unique+ 默认删除器;真需要定制,用无状态函数对象更稳妥
C++ 标准库对 unique_ptr 的实现非常精巧,但它的“唯一性”和“不可拷贝”不是语法糖,而是编译期强制契约——一旦在接口中暴露 unique_ptr,就等于向调用方明确声明:“这个资源,我交给你,之后就不管了”。很多人卡在 move 语义不熟、误用 release、或给数组配错模板参数上。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











