怎样处理C++中的环形引用问题 weak_ptr打破循环引用技巧

环形引用指两个或多个shared_ptr相互引用导致内存泄漏。例如,结构体a和b各自持有对方的shared_ptr,当main函数结束时,它们的引用计数均不为0,无法释放。解决方法是使用weak_ptr打破循环,weak_ptr不会增加引用计数,仅观察对象。其使用步骤包括:1. 将其中一个shared_ptr改为weak_ptr;2. 使用lock()转换为shared_ptr并检查对象是否存在;3. 在合适场景如观察者模式、缓存系统、树结构中应用。注意事项包括避免频繁调用lock()、控制shared_ptr生命周期。

怎样处理C++中的环形引用问题 weak_ptr打破循环引用技巧

c++中使用智能指针时,shared_ptr虽然能自动管理内存,但如果对象之间存在相互引用,就很容易导致环形引用(循环引用),从而引发内存泄漏。这时候,就需要用到weak_ptr来打破这个循环。

怎样处理C++中的环形引用问题 weak_ptr打破循环引用技巧


什么是环形引用?

环形引用指的是两个或多个shared_ptr对象彼此持有对方的引用,导致它们的引用计数永远不为0,即使这些对象已经不再被外部使用,也无法释放。

怎样处理C++中的环形引用问题 weak_ptr打破循环引用技巧

举个常见的例子:

立即学习C++免费学习笔记(深入)”;

struct B;  struct A {     shared_ptr<B> b_ptr; };  struct B {     shared_ptr<A> a_ptr; };  int main() {     auto a = make_shared<A>();     auto b = make_shared<B>();     a->b_ptr = b;     b->a_ptr = a; }

在这个例子中,a和b互相通过shared_ptr持有对方,当main函数结束时,a和b的引用计数都还是1,所以不会释放,造成内存泄漏。

怎样处理C++中的环形引用问题 weak_ptr打破循环引用技巧


weak_ptr的作用:打破循环

weak_ptr是一个弱引用指针,它不会增加所指向对象的引用计数。也就是说,它观察一个由shared_ptr管理的对象,但不会阻止该对象被销毁。

解决上面的例子,只需要将其中一个引用改为weak_ptr:

struct A;  struct B {     weak_ptr<A> a_ptr;  // 改为 weak_ptr };  struct A {     shared_ptr<B> b_ptr; };

这样,当a被释放时,b中的a_ptr会变成“过期”的状态,而不会影响对象的释放流程。


如何正确使用weak_ptr?

使用weak_ptr需要注意几个关键点:

  • 不能直接访问对象内容,需要先转换成shared_ptr
  • 转换前要检查对象是否还存在(是否“过期”)

基本使用步骤如下:

weak_ptr<A> wp;  {     shared_ptr<A> sp = make_shared<A>();     wp = sp; }  // sp离开作用域后,对象被释放  if (auto sp = wp.lock()) {  // lock()返回shared_ptr     // 使用sp操作对象 } else {     // 对象已被释放 }

常见使用场景包括:

  • 观察者模式中,避免订阅者和发布者之间形成循环引用
  • 缓存系统中,避免缓存项长期持有资源
  • 树结构中父子节点之间的引用关系

小技巧与注意事项

  • 如果你发现某个对象应该被释放却没有释放,可以怀疑是不是出现了循环引用。
  • weak_ptr本身没有“悬空”问题,因为它不拥有对象所有权。
  • lock()返回的shared_ptr会延长对象生命周期,适合在线程环境中安全地访问对象。
  • 不要频繁调用lock()并重复使用同一个shared_ptr副本,应尽量控制其生命周期范围。

总的来说,处理C++中的环形引用问题,最常用也最有效的方法就是使用weak_ptr来替代其中一环的shared_ptr。虽然看起来简单,但在设计复杂对象关系时非常实用。
基本上就这些。

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享