跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

C++中的std::shared_ptr是什么?(如何处理循环引用问题)

std::shared_ptr通过控制块维护共享计数和弱引用计数,仅当二者均为0时才释放资源;循环引用时因彼此强引用致共享计数永不归零,对象无法析构,导致内存持续增长。 std::shared_ptr 的引用计数机制是怎么工作的 它靠内部一个控制块(control block)维护两个计数:指向对象的
shared_ptr
数量,和指向该控制块的
weak_ptr
数量。只有当这两个计数都归零,资源才会被释放。 关键点在于:只要还有任意一个
shared_ptr
持有对象,对象就不会析构;而
weak_ptr
不影响对象生命周期,只影响控制块本身是否销毁。 每次拷贝
shared_ptr
,共享计数 +1;析构或赋值覆盖时 -1
weak_ptr
构造/拷贝只增控制块引用计数,不增共享计数 调用
weak_ptr::lock()
返回临时
shared_ptr
,会短暂增加共享计数 循环引用发生时具体卡在哪一步 典型场景是两个对象互相用
shared_ptr
持有对方,比如父子节点、观察者与被观察者。它们的控制块彼此强引用,导致共享计数永远不为 0 —— 即使外部所有
shared_ptr
都已离开作用域,对象也无法释放。 现象很直接:程序运行中内存持续上涨,Valgrind 或 ASan 可能报“still reachable”块,但不会崩溃;你查
shared_ptr::use_count()
会发现始终 ≥2。 立即学习 “ C++免费学习笔记(深入) ”; C知道 CSDN推出的一款AI技术问答工具 下载 不是内存泄漏检测工具报错,而是对象生命周期被意外延长 调试时可在析构函数里打日志,确认它们根本没被调用 用
std::cout 快速验证是否存在滞留引用
用
std::weak_ptr
打断循环的具体写法 把循环中「非拥有关系」那一端换成
weak_ptr
。比如父节点拥有子节点(
shared_ptr
),子节点反向持有父节点就该用
weak_ptr
;访问前调用
lock()
转成临时
shared_ptr
,确保安全。
struct Node { std::shared_ptr parent; std::vector> children; }; // ❌ 错误:children 里每个 node 又持有 parent 的 shared_ptr // ✅ 正确:parent 改成 weak_ptr,访问时 lock() struct Node { std::weak_ptr parent; std::vector> children; };
weak_ptr::lock()
返回
shared_ptr
,若原对象已析构则返回空指针,必须判空 不要在构造函数里直接调用
lock()
并保存结果——可能引发初始化顺序问题 避免在 lambda 捕获中隐式提升
weak_ptr
为
shared_ptr
后长期持有 还有哪些容易被忽略的循环引用场景 除了显式的对象互持,闭包、回调、缓存结构也常埋雷。比如用
std::function
存绑定了
this
的 lambda,而
this
又被某个
shared_ptr
管理 —— 这时 lambda 实际持有了
shared_ptr
,形成闭环。 用
[this]
捕获时,如果
this
所在对象由
shared_ptr
管理,等价于捕获了
shared_ptr
改用
[weak_this = weak_from_this()]
(需继承
std::enable_shared_from_this
),并在 lambda 内部
auto ptr = weak_this.lock(); if (!ptr) return;
缓存容器(如
std::map>
)若同时被缓存项反向引用,也会锁死 最麻烦的不是写错,而是循环不发生在同一层代码里 —— 它可能跨多个模块、经过信号槽或线程队列。一旦怀疑,优先检查所有
shared_ptr
成员变量和回调绑定逻辑。

相关文章