shared_ptr通过引用计数机制管理对象生命周期,每个shared_ptr共享一个控制块,其中记录强引用计数,当强引用计数为0时自动释放资源;循环引用问题发生在多个对象相互以shared_ptr持有对方,导致引用计数无法归零,内存无法释放,例如父子节点间双向强引用;解决方法是将一方改为使用weak_ptr,weak_ptr不增加强引用计数,仅作为观察者,通过lock()安全访问对象,从而打破循环,确保内存正确释放。

shared_ptr
的引用计数机制和循环引用问题是 C++ 智能指针使用中的核心知识点,理解它们对避免内存泄漏至关重要。
shared_ptr 引用计数是怎么工作的
shared_ptr
通过引用计数来管理动态对象的生命周期。每个
shared_ptr
实例指向一个资源(堆上分配的对象),同时共享一个控制块(control block),这个控制块中保存了:
指向实际对象的指针当前有多少个
shared_ptr
共享该对象(强引用计数)当前有多少个
weak_ptr
观察该对象(弱引用计数)可能还包括自定义删除器、分配器等信息
每当一个新的
shared_ptr
被创建并指向同一个对象时,强引用计数加 1;当一个
shared_ptr
被销毁或重新赋值时,强引用计数减 1。当强引用计数变为 0 时,说明没有
shared_ptr
再持有该对象,此时会自动调用删除器释放对象内存。
举个简单例子:
std::shared_ptr p1 = std::make_shared(42);std::shared_ptr p2 = p1; // 引用计数变为 2p1.reset(); // 引用计数减为 1,对象未释放// p2 仍然有效,直到它也被销毁
引用计数的操作是线程安全的(原子操作),多个线程同时增加或减少引用不会导致数据竞争。
循环引用问题是怎么产生的
循环引用发生在两个或多个对象通过
shared_ptr
相互持有对方,导致引用计数永远无法归零,从而造成内存泄漏。
典型场景是父子节点结构中,父节点用
shared_ptr
持有子节点,子节点也用
shared_ptr
持有父节点:
struct Node;struct Node { std::shared_ptr parent; std::shared_ptr child;};auto node1 = std::make_shared();auto node2 = std::make_shared();node1->child = node2;node2->parent = node1; // 形成循环引用
此时:
node1
的引用计数为 2(外部指针 + node2 的 parent)
node2
的引用计数为 2(外部指针 + node1 的 child)
当
node1
和
node2
离开作用域后,外部引用消失,引用计数各减为 1,但由于彼此还持有对方,计数不为 0,对象不会被销毁,造成内存泄漏。
解决循环引用的方案:使用 weak_ptr
最标准的解决方案是:把循环中的一方改为使用
weak_ptr
。
weak_ptr
是一种不增加引用计数的观察者指针,它指向
shared_ptr
管理的对象,但不影响其生命周期。只有在需要访问对象时,才通过
lock()
方法尝试获取一个临时的
shared_ptr
。
修改上面的例子:
struct Node { std::shared_ptr child; std::weak_ptr parent; // 改为 weak_ptr};auto node1 = std::make_shared();auto node2 = std::make_shared();node1->child = node2;node2->parent = node1; // weak_ptr 不增加引用计数// 访问父节点时:if (auto parent = node2->parent.lock()) { // parent 是临时的 shared_ptr,安全访问 std::cout << "Has parentn";} else { std::cout << "Parent is gonen";}
这样:
node1
持有
node2
(强引用)
node2
只“观察”
node1
(弱引用)当外部指针释放后,
node2
的引用计数变为 0,被销毁接着
node1
的引用计数也变为 0,被销毁内存正确释放,无泄漏
使用建议和注意事项
谁该用 weak_ptr? 通常“从属”或“反向”引用的一方使用
weak_ptr
。比如父持有子用
shared_ptr
,子指向父用
weak_ptr
。
weak_ptr
不能直接解引用,必须通过
lock()
获取
shared_ptr
。
lock()
返回一个
shared_ptr
,如果原对象已释放,则返回空
shared_ptr
。
weak_ptr
本身也有引用计数(弱引用计数),用于管理控制块的销毁时机。避免在
weak_ptr
上做
expired()
判断后再
lock()
,因为可能有竞态条件,直接
lock()
更安全。
基本上就这些。关键点是:shared_ptr 靠引用计数自动管理生命周期,但循环引用会打破这个机制,用 weak_ptr 打破循环是最标准做法。
以上就是shared_ptr引用计数怎样工作 循环引用问题解决方案的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1471863.html
微信扫一扫
支付宝扫一扫