该用 std::unique_ptr 而不是 std::shared_ptr 时:所有权明确、无需共享,追求零开销、防重复释放与悬挂指针;单对象生命周期由当前作用域或类成员完全控制;函数返回动态对象应优先返回 unique_ptr,调用方可按需转换。

什么时候该用 std::unique_ptr 而不是 std::shared_ptr
所有权明确、不需要共享时,std::unique_ptr 是首选。它零开销、不可复制、可移动,天然防止重复释放和悬挂指针。
常见错误是看到“自动管理”就默认选 shared_ptr——结果引入不必要的引用计数开销,还可能因循环引用导致内存泄漏。
- 单个对象生命周期由当前作用域或类成员完全控制 → 用
unique_ptr - 需要多个所有者(如观察者模式中多个对象持有同一资源)→ 才考虑
shared_ptr - 函数返回动态对象?优先返回
unique_ptr,调用方按需转成shared_ptr(用std::move或std::shared_ptr<t>(ptr)</t>)
std::shared_ptr 循环引用怎么破
两个 shared_ptr 相互持有对方所指对象,引用计数永远不为 0,内存永不释放——这是最典型的智能指针误用。
根本解法是打破强引用链:一端改用 std::weak_ptr。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-
weak_ptr不增加引用计数,访问前必须调用lock()转成shared_ptr,失败说明对象已销毁 - 父子关系中,子对象持父的
weak_ptr,而非shared_ptr - 回调或观察者注册时,存储
weak_ptr,触发前先lock()检查有效性
示例:auto p = parent.lock(); if (p) { p->do_something(); }
从裸指针或 new 迁移到智能指针的坑
直接把 new 结果塞进智能指针看似安全,但容易忽略构造时机和异常安全。
- 避免写
shared_ptr<t>(new T(args))</t>—— 若T构造抛异常,new分配的内存会泄漏;改用make_shared<t>(args)</t>(unique_ptr同理用make_unique) - 不要用同一个裸指针多次初始化不同智能指针,比如
shared_ptr<int>(p)</int>和shared_ptr<int>(p)</int>→ 双重释放 - 类成员是
unique_ptr时,别手动reset(new T);优先用make_unique或移动赋值
智能指针不能解决的所有权边界模糊问题
智能指针管的是“谁负责释放”,不是“谁有权访问”。如果多个模块通过 get() 拿到裸指针并长期缓存,对象被释放后仍访问,照样崩溃。
-
get()返回的裸指针仅限临时使用,绝不能存储或跨函数传递 - 容器里存
shared_ptr没问题,但若存shared_ptr.get(),等于又回到手动管理 - 函数参数该传
const shared_ptr<t>&</t>还是T*?前者表示参与所有权,后者仅表示临时借用——语义必须清晰
真正难的从来不是语法,而是设计时就定清楚:这个指针的生命周期归谁管、哪些地方只是看一眼、哪些地方要确保对象活着。智能指针只是把这个契约变成编译器能检查的东西。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










