shared_ptr适用于多模块共享中心化对象、树/图结构复用子资源、回调/观察者跨生命周期持有对象等场景;需避免误用于函数参数、容器元素和局部临时对象。

多个模块需要同时持有同一对象时用 shared_ptr
当配置管理器、资源池、事件总线这类中心化对象被多个业务模块(如 UI 控件、网络层、日志系统)同时引用,且各自生命周期不一致时,shared_ptr 是合理选择。裸指针或 unique_ptr 无法安全支持这种“谁最后一个离开谁释放”的语义。
常见错误现象:double free 或 use-after-free,尤其在模块解耦、插件式架构中高频出现。
- 必须确保所有持有者都通过
shared_ptr访问,不能混用裸指针或unique_ptr - 避免从裸指针(如
new T())直接构造,优先用std::make_shared<t>()</t>,减少一次内存分配 - 若对象需跨线程传递,
shared_ptr的引用计数操作是原子的,但对象本身读写仍需额外同步
树/图结构中节点共享子资源时用 shared_ptr
比如场景:一个 Scene 对象包含多个 Node,每个 Node 可能引用同一份纹理、材质或动画数据;或者图中多个节点指向同一个缓存块。此时用 shared_ptr 能自然表达“资源被复用”的语义,且自动回收。
性能影响:每次拷贝或赋值都会触发原子增减引用计数,高频小对象频繁共享会带来可观开销。
- 不要为单个节点内部临时计算临时量使用
shared_ptr,那是unique_ptr或栈对象的职责 - 若图结构存在父子双向引用(如父节点存子节点
shared_ptr,子节点又存父节点shared_ptr),必须用weak_ptr打破循环,否则内存永不释放 -
shared_ptr管理数组需显式传入删除器,例如:std::shared_ptr<int>(new int[10], std::default_delete<int>())</int></int>
回调、观察者、异步任务中传递对象所有权时用 shared_ptr
典型场景:注册一个异步 HTTP 请求完成回调,回调里要访问某个业务对象;或观察者模式中多个监听器订阅同一个主题对象。这些回调/监听器可能比原始对象活得更久,shared_ptr 能保证对象在回调执行时依然有效。
容易踩的坑:把 this 直接转成 shared_ptr 传进 lambda —— 若类没继承 std::enable_shared_from_this,会触发未定义行为。
- 类需继承
std::enable_shared_from_this<t></t>,并在已有shared_ptr持有时调用shared_from_this() - lambda 捕获
shared_ptr而非this,避免悬空和循环引用 - 异步任务中若只读不修改对象,可考虑用
weak_ptr延迟升级,避免意外延长生命周期
什么时候绝对不用 shared_ptr
不是所有“多个地方用到”都该上 shared_ptr。它引入引用计数开销、增加调试复杂度,并掩盖所有权模糊问题。
最常被误用的三类情况:函数参数传递、容器元素、局部临时对象。这些多数时候该用引用、unique_ptr 或栈对象。
- 函数入参:优先用
const T&或T&;仅当函数需延长对象生命周期(如塞进后台队列)才考虑shared_ptr - 容器存对象:若容器完全拥有元素,用
std::vector<:unique_ptr>></:unique_ptr>;若只是观察,用std::vector<t></t>或std::vector<:weak_ptr>></:weak_ptr> - 局部作用域内:对象生命周期明确短于当前作用域,用栈对象或
unique_ptr即可,shared_ptr纯属冗余
真正关键的判断点不在“有没有多个指针”,而在于“有没有多个独立的、不可协调的生命周期需要共同决定对象的销毁时机”。一旦这个条件不成立,shared_ptr 就是过重的设计。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











