该用 std::unique_ptr 而不是 std::shared_ptr 的核心判断标准是所有权是否需要共享:unique_ptr 表示独占所有权,转移后原指针自动置空;shared_ptr 通过引用计数允许多方共享同一对象。

什么时候该用 std::unique_ptr 而不是 std::shared_ptr
核心判断标准是所有权是否需要共享:std::unique_ptr 表示独占所有权,转移后原指针自动置空;std::shared_ptr 通过引用计数允许多方共享同一对象。如果只是临时封装堆内存、函数返回资源、或作为容器元素管理单个对象,优先选 unique_ptr —— 它零开销、无原子操作、不引入循环引用风险。
常见误用场景:
- 把
shared_ptr当作“更安全的unique_ptr”随意传参,结果拖慢性能且掩盖设计问题 - 在类成员中用
shared_ptr管理本该由类自身生命周期决定的对象(比如内部缓存),导致对象存活时间超出预期 - 跨线程传递
shared_ptr但没意识到控制块(control block)的引用计数是原子操作,有轻微开销
unique_ptr 的正确构造与移动语义
unique_ptr 不支持拷贝,只支持移动。直接赋值或传参时若忘记 std::move(),编译器会报错:use of deleted function 'std::unique_ptr。
推荐写法:
立即学习“C++免费学习笔记(深入)”;
- 用
std::make_unique构造,避免裸(...) new和异常安全问题 - 函数返回
unique_ptr时,直接return std::make_unique,调用方自动接收右值(x); - 传入函数时,按值接收(
void f(std::unique_ptr),表示函数要接管所有权;如只需观察,改用p) T*或T&
std::unique_ptrp1 = std::make_unique (42); std::unique_ptr p2 = std::move(p1); // ✅ 正确:显式移动 // std::unique_ptr p3 = p1; // ❌ 编译失败
shared_ptr 的循环引用与 weak_ptr 解法
两个 shared_ptr 相互持有对方管理的对象(例如双向链表节点、父子对象关系),会导致引用计数永远不为 0,内存泄漏。这是 shared_ptr 最典型的陷阱。
解决方式不是少用 shared_ptr,而是识别“观测性引用”并替换为 std::weak_ptr:
-
weak_ptr不增加引用计数,调用lock()可尝试升级为shared_ptr,失败说明对象已销毁 - 典型模式:父持子用
shared_ptr,子持父用weak_ptr -
weak_ptr本身不保证线程安全,但lock()是原子的
struct Node {
std::shared_ptr next;
std::weak_ptr prev; // 避免循环引用
};
auto a = std::make_shared();
auto b = std::make_shared();
a->next = b;
b->prev = a; // ✅ 不增加 a 的引用计数
自定义删除器与数组支持的细节差异
unique_ptr 和 shared_ptr 都支持自定义删除器,但语法和默认行为不同:
-
unique_ptr是合法类型,析构时自动调用delete[];而shared_ptr在 C++17 前不被标准支持,C++17 起才允许,但仍需显式传入default_delete - 自定义删除器类型是
unique_ptr模板参数的一部分(影响类型),所以unique_ptr;而!= unique_ptr shared_ptr的删除器是运行期绑定的,类型不变 - 用
make_shared无法指定自定义删除器,必须用裸new+ 构造函数方式
// unique_ptr 数组(推荐) std::unique_ptr真正难的不是记住语法,是每次写arr = std::make_unique (10); // shared_ptr 数组(C++17+) std::shared_ptr arr2(new int[10], std::default_delete ());
shared_ptr 时多问一句:“这个对象,到底有没有别的地方也该负责它的生死?”——没想清楚就先用 unique_ptr。











