当前位置: 首页 > news >正文

图解C++智能指针的循环引用

欢迎大家访问我的个人主页guts的小屋

循环引用是学习智能指针过程中的一个小难点,笔者愚钝,明明知道是两个指针互相引用导致了内存泄漏,但看各种文字资料时,脑子里总是一团浆糊,感觉似懂非懂,于是自己绘制了几张图片,思路和概念才清晰起来,希望这篇博客能帮助有同样困惑的同学解惑。

shared_ptr 的实现

要明白循环引用,我们首先需要知道shared_ptr是如何实现的,它内部包含两个指针,一个指向我们要管理的资源对象,另一个指向则指向这个对象所对应的控制块。在这个控制块中,包含了这个对象的强引用计数,当我们第一次声明一个shared_ptr变量时,就会为这个资源对象分配一个控制块,并将强引用计数初始化为1。
当有新的shared_ptr对象拥有这个资源的时候,强引用计数就会+1,当shared_ptr对象销毁或者调用.reset()方法时,强引用计数就会-1,当强引用计数为0时,这个资源就会析构。这样我们就通过RAII来避免了内存的泄露。

循环引用

这看起来是一个很完美的设计,但是shared_ptr管理的资源和shared_ptr对象本身是两块内存,并且shared_ptr管理的资源本身也可以包含一个shared_ptr对象并持有资源,这样就会导致,一旦两个资源本身互相持有,那么他们析构的条件就是对方析构,这就会导致内存的泄露。

这么说可能有些模糊,我们直接来看例子:

class A {
public:std::shared_ptr<A> ptr;...
}
int main() {// code Istd::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<A> b = std::make_shared<A>(); // code IIa->ptr = b;b->ptr = a;return 0;
}

code I 运行完毕后,我们会在堆中创建两个A类的对象,我们将其称之为OBJ_AOBJ_B,同时栈上会有两个std::shared_ptr<A>对象ab,他们分别指向OBJ_AOBJ_B以及他们各自的控制块CB_ACB_B,如下图所示
image
此时两个控制块的引用计数都是1,当我们运行Code II后,OBJ_AOBJ_B会拥有对对方的所有权,他们的引用计数都变为2
image
这个时候代码运行完毕,ab作为栈上的对象被销毁,OBJ_AOBJ_B的引用计数都下降为1,但是由于此时他们都持有指向彼此的shared_ptr对象,引用计数仍为1,不会继续下降,这块内存就不会被释放,造成了内存泄露。
image

weak_ptr的引入

为了解决这个问题,我们引入了weak_ptr,它不拥有资源的所有权,指向资源的时候只会增加弱引用计数而非强引用计数,弱引用计数不决定资源的生命周期,只决定控制块的生命周期,当弱引用计数为0时销毁控制块。
需要注意的是,shared_ptr也会让弱引用计数+1。

class A {
public:std::weak_ptr<A> ptr;...
}
int main() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<A> b = std::make_shared<A>();a->ptr = b;b->ptr = a;return 0;
}

当我们使用上面的代码时,几个对象的声明周期如下图所示:
image
ba离开作用域后

  1. b析构,CB_B的强引用计数-1为0,弱引用计数-1=1

  2. OBJ_B析构,CB_A的弱引用计数-1=1

  3. a析构,CB_A的强引用计数-1为0,弱引用计数-1=0

  4. OBJ_ACB_A析构,CB_B的弱引用计数-1=0

  5. CB_B析构

http://www.hskmm.com/?act=detail&tid=26186

相关文章:

  • 国庆收心指南:用AI提示词工程解决节后综合征
  • CF1895F Fancy Arrays
  • 文件系统的全局结构
  • 2025.10.7
  • 自由型象棋分析程序
  • 前端HTML contenteditable 属性使用指南 - 教程
  • luogu P1648 看守
  • 题解:P11219 【MX-S4-T3】「yyOI R2」youyou 的序列 II
  • Seismic Unix 基础使用
  • 2025实验室净化厂家/实验室装修厂家/实验室建设厂家权威推荐榜:专业设计与洁净技术实力之选
  • 修改注册表,实现电脑小键盘开机自启(NumLock灯常亮)
  • 完整教程:nav2笔记-250603
  • Bartender打印乱序条码教程
  • 多Agent协作入门:基于A2A协议的Agent通信
  • 时尚产品需求预测与库存优化模型解析
  • 自制带得分和推荐走法的象棋视频
  • DP分析黑科技——闫氏DP分析法
  • MUGEN游戏引擎等一系列相关杂谈
  • # 20232313 2025-2026-1 《网络与系统攻防技术》实验一实验报告 - 20232313
  • 一生一芯学习:PA2:输入输出
  • vector使用中的一个小问题
  • OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering() - 指南
  • 2025.10.7——2绿
  • 完整教程:无人机避障——感知部分(Ubuntu 20.04 复现Vins Fusion跑数据集)胎教级教程
  • 我真的博了
  • 2025.10.6——1绿1蓝
  • 深入解析:人工智能-Chain of Thought Prompting(思维链提示,简称CoT)
  • 年龄排序
  • 二分图最大匹配 输出具体方案
  • 我的联想小新潮7000笔记本的优化