unique_ptr不可拷贝只能移动,应使用std::make_unique创建以确保异常安全;转移所有权必须显式std::move;访问前需判空,支持->和*操作符。

unique_ptr 不能被拷贝,只能移动 —— 这是它和 shared_ptr 最根本的区别,也是你写错时编译器报 use of deleted function 的直接原因。
怎么创建 unique_ptr(别用 new 手动传)
推荐用 std::make_unique,它更安全、异常安全,且语义清晰:
auto ptr = std::make_unique<int>(42); // OK auto arr = std::make_unique<int[]>(10); // 数组版,注意 [] 语法
避免这样写:
std::unique_ptr<int> bad{new int(42)}; // 不推荐:可能内存泄漏(如构造函数抛异常)
-
make_unique一次性完成内存分配和对象构造,中间不会被异常打断 - 数组版本必须写
<T[]>,否则析构时调用delete而非delete[],UB - 不能用
make_unique构造私有构造函数或 explicit 构造函数的类(需显式 new + 转移)
怎么转移所有权(move 是唯一合法方式)
unique_ptr 没有拷贝构造函数和拷贝赋值运算符,所有“传递”都必须显式 move:
立即学习“C++免费学习笔记(深入)”;
auto p1 = std::make_unique<std::string>("hello");
auto p2 = std::move(p1); // OK:p1 变成 nullptr,p2 拿走资源
// auto p3 = p1; // 编译错误:calling a deleted function
- 函数参数传
unique_ptr时,也必须用std::unique_ptr<T>&&或直接值传递(触发 move) - 返回
unique_ptr没问题:NRVO 或 move 会自动处理,无需std::move包裹返回值 - 容器里存
unique_ptr(如std::vector<std::unique_ptr<T>>)是常见模式,插入必须用push_back(std::move(p))
怎么访问对象(-> 和 * 都行,但别解引用空指针)
行为和原生指针一致,但多了空安全性检查习惯:
auto p = std::make_unique<int>(100);
if (p) { // 等价于 if (p != nullptr)
std::cout << *p << " " << p->get(); // *p 解引用,p->get() 返回原始指针
}
p.reset(); // 显式释放,p 变成 nullptr
-
p.get()返回裸指针,仅用于传给 C API 或需要临时绕过 RAII 的场景;不要拿它去delete -
p.release()交出控制权,返回裸指针,且p变成nullptr;之后你要自己负责释放 - 访问前不检查是否为空,运行时可能 crash —— 尤其在
reset()或 move 后忘了置空逻辑
自定义删除器怎么写(小众但关键)
默认用 delete,但对 FILE*、malloc 分配内存、C API 对象等,必须指定删除逻辑:
// 用 malloc 分配的 int
auto p = std::unique_ptr<int, void(*)(void*)>{
static_cast<int*>(std::malloc(sizeof(int))),
[](void* ptr) { std::free(ptr); }
};
<p>// 或用函数对象(更常见)
struct FreeDeleter {
void operator()(void* p) const { std::free(p); }
};
std::unique_ptr<void, FreeDeleter> p2{std::malloc(1024)};
- 删除器类型是模板参数一部分:
unique_ptr<T, D>,类型不同 → 类型不同 → 不能赋值或比较 - lambda 若含捕获(
[x]),无法作为模板参数(非可默认构造),得用函数对象或std::function(但后者有额外开销) - 数组版 + 自定义删除器必须写
unique_ptr<T[], D>,且删除器要适配delete[]或 free
最易忽略的是数组语法和删除器类型的绑定关系 —— 写错一个符号(比如漏掉 [] 或删器没匹配 delete[]),程序可能跑得通但行为未定义。










