(C++) share_ptr 之循环引用

文章目录

  • 🚩前言
  • 🚩循环引用
    • 🕹️例子1
      • Code
      • 😭shared_ptr (错误)
      • 😂weak_ptr (正确)
      • 😭unique_ptr (错误)
    • 🕹️例子2
      • Code
  • 🚩END

🚩前言

自C++11起,有三大智能指针:

  • unique_ptr
  • shared_ptr
  • weak_ptr

都是内存管理中的非常重要的一部分动态内存管理 - cppreference.com。

其中shared_ptr在实际应用中具有非常广泛的应用。

而由于其较unique_ptr的功能多,有引用计数的概念。导致存在一个名为循环引用的问题。这是一个非常经典的坑。但解决方案也不是很复杂。下面来一起看看吧。

🚩循环引用

🕹️例子1

Code

#include <iostream>
#include <memory>struct A;
struct B;struct A {std::shared_ptr<B> ptr;A() {std::cout << __func__ << std::endl;}~A() {std::cout << __func__ << std::endl;}
};struct B {std::shared_ptr<A> ptr;B() {std::cout << __func__ << std::endl;}~B() {std::cout << __func__ << std::endl;}
};int main() {auto pa = std::make_shared<A>();auto pb = std::make_shared<B>();pa->ptr = pb;pb->ptr = pa;
}

😭shared_ptr (错误)

此代码,分别有两个类,class Aclass B。其中都有一个成员都是零一个类型的shared_ptr。

分别构造好对象后都去执行另一个对象,这就产生了著名的循环引用问题。

下面是输出结果,未能执行析构函数,导致内存泄漏

# 输出结果
A
B

逐步分析

在这里插入图片描述

当两个对象构造完毕后,两块share_ptr的智能指针的引用计数都为1,且内部的智能指针对象是默认空构造。

接着让两个内部的shared_ptr都指向对方,此时引用计数都为2。

此后,脱离作用域,开始RAII回收资源

此时两个在main函数中的shared_ptr对象都回收,两个引用计数都-1,变为1。

然后,就没有然后了。因此引用计数不为0,导致实际的对象内存不符合智能指针的要求,无法释放。

😂weak_ptr (正确)

假如此时将class B的智能指针改为weak_ptr。即可解决该问题。

weak_ptr其不会增加智能指针的引用计数,当实际需要使用的时候再构造出shared_ptr。这就是一种视图的作用。

逐步分析

在这里插入图片描述

当两个对象构造完毕后,两块share_ptr的智能指针的引用计数都为1,且内部的智能指针对象是默认空构造。

然后,B的weak指向A,A的引用计数块不变;A的share指向B,B的引用计数+1。Bcnt=2,Acnt=1

此后,脱离作用域,开始RAII回收资源

此时两个在main函数中的shared_ptr对象都回收,Bcnt=1Acnt=0

此时A符合回收条件,A资源释放,在A中的share也释放,B的计数-1=0。Bcnt=0

然后B也符合释放条件,B释放。

最终资源全部正确回收。

😭unique_ptr (错误)

我们再做一个假设,如果把class B的智能指针改为unique_ptr,会发生什么。

先公布答案:

A
B
~A
~B
~A

当然主函数也要略做修改,因为shared_ptr不能直接转化为unique_ptr。

int main() {auto pa = std::make_shared<A>();auto pb = std::make_shared<B>();pa->ptr = pb;pb->ptr.reset(pa.get());
}

当然看到这里,敏感的朋友会直接发现这根本就是一种错误的编码。因为pa的对象直接让两个可以掌管生命周期的对象把控了。

所以,这就出现了A对象的两次析构。

这里的原理分析与上面类似,这里就不做过多展开了。

🕹️例子2

Code

除了定义两个不同的类,有可能会产生循环引用,一个类自身也是有可能会产生的。

#include <iostream>
#include <memory>struct Node {std::shared_ptr<Node> ptr;Node() {std::cout << __func__ << std::endl;}~Node() {std::cout << __func__ << std::endl;}
};int main() {auto p = std::make_shared<Node>();p->ptr = p;
}

这里解决方案一样,将内部的shared_ptr改为weak_ptr即可。

而要是这里改为unique_ptr时,有趣的事情就发生了,这里直接无限递归调用析构函数。

因为,当shared_ptr对象回收时,引用计数归0,导致Node对象回收。

当析构函数结束后,类内的unique_ptr也是回收资源,它指向的也是当前对象,因此再次delete,也就是执行析构函数,析构成员

自此不断递归。。。

在这里插入图片描述

🚩END

关注我,学习更多C/C++,算法,计算机知识

B站:

👨‍💻主页:天赐细莲 bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/828671.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vu3+QuaggaJs实现web页面识别条形码

一、什么是QuaggaJs QuaggaJS是一个基于JavaScript的开源图像识别库&#xff0c;可用于识别条形码。 QuaggaJs的作用主要体现在以下几个方面&#xff1a; 实时图像处理与识别&#xff1a;QuaggaJs是一款基于JavaScript的开源库&#xff0c;它允许在Web浏览器中实现实时的图像…

LORA详解

参考论文&#xff1a; low rank adaption of llm 背景介绍&#xff1a; 自然语言处理的一个重要范式包括对一般领域数据的大规模预训练和对特定任务或领域的适应处理。在自然语言处理中的许多应用依赖于将一个大规模的预训练语言模型适配到多个下游应用上。这种适配通常是通过…

DiT论文精读Scalable Diffusion Models with Transformers CVPR2023

Scalable Diffusion Models with Transformers CVPR2023 Abstract idea 将UNet架构用Transformer代替。并且分析其可扩展性。 并且实验证明通过增加transformer的宽度和深度&#xff0c;有效降低FID 我们最大的DiT-XL/2模型在classconditional ImageNet 512、512和256、256基…

小程序AI智能名片S2B2C商城系统:四大主流商业模式深度解析与实战案例分享

在私域电商迅速崛起的大背景下&#xff0c;小程序AI智能名片S2B2C商城系统以其独特的商业模式和强大的功能&#xff0c;正成为品牌商们争相探索的新领域。在这一系统中&#xff0c;拼团模式、会员电商、社区团购和KOC营销等四种主流模式&#xff0c;为品牌商提供了多样化的营销…

【文章转载】Lance Martin的关于RAG的笔记

转载自微博黄建同学 从头开始学习 RAG&#xff0c;看Lance Martin的这篇笔记就行了&#xff0c;包含了十几篇论文和开源实现&#xff01; —— 这是一组简短的&#xff08;5-10 分钟视频&#xff09;和笔记&#xff0c;解释了我最喜欢的十几篇 RAG 论文。我自己尝试实现每个想…

C# GetField 方法应用实例

目录 关于 C# Type 类 GetField 方法应用 应用举例 心理CT设计题 类设计 DPCT类实现代码 小结 关于 C# Type 类 Type表示类型声明&#xff1a;类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义&#xff0c;以及开放或封闭构造的泛型类型。调用 t…

WPS-EXCEL:快速删除多个线条对象

问题图 我需要将线条快速删除 方法一:使用定位对象功能 使用定位功能&#xff1a;按Ctrl G打开定位对话框。在对话框中&#xff0c;点击“定位条件”。 定位对象&#xff1a;在定位条件对话框中&#xff0c;勾选“对象”选项&#xff0c;然后点击“确定”。这样&#xff0c;…

CTF之变量1

拿到题目发现是一个php代码&#xff0c;意思是用get方式获取args参数。 至于下面那个正则表达式怎么绕过暂且不知&#xff0c;但是题目最上面告诉我们lag In the variable ! &#xff08;意思是flag就在变量中&#xff09;。 那我们就传入全局变量globals&#xff08;&#xf…

户外指南——时代产物

分类 一级分类&#xff1a; 衣&#xff1a;除了上述提到的&#xff0c;还包括衣物的材质、款式多样性、与身份地位的关联等。 食&#xff1a;还包括饮食的文化内涵、地域特色、对特殊饮食需求的满足等。 住&#xff1a;还包括居住空间的合理布局、智能家居的应用、与自然环境…

B2B企业如何做好谷歌Google广告推广营销布局?

当今全球化的商业环境中&#xff0c;B2B企业要想在激烈的市场竞争中脱颖而出&#xff0c;拓展海外市场成为了必经之路。而谷歌Google广告&#xff0c;作为全球最大的在线广告平台&#xff0c;无疑是企业触达全球潜在客户的黄金钥匙。云衔科技通过专业服务助力企业轻松开户与高效…

Java苍穹外卖01-开发环境搭建(Git、nginx)-Swagger-员工管理

一、开发环境搭建 1.项目架构 2.Git版本管理 在IDEA中可以一键搭建并commit&#xff0c;当Git远程仓库搭建后就可以push 3.前后端联调 Builder注解&#xff1a; 加了注解后就可以通过这样的方式创建对象 接收传入的是dto对象&#xff0c;传出去的对象为vo对象 4.nginx反向…

4.18.2 EfficientViT:具有级联组注意力的内存高效Vision Transformer

现有Transformer模型的速度通常受到内存低效操作的限制&#xff0c;尤其是MHSA&#xff08;多头自注意力&#xff09;中的张量整形和逐元素函数。 设计了一种具有三明治布局的新构建块&#xff0c;即在高效FFN&#xff08;前馈&#xff09;层之间使用单个内存绑定的MHSA&#x…

【STM32HAL库】外部中断

目录 一、中断简介 二、NVIC 1.寄存器 2.工作原理 3.优先级 4.使用NVIC 三、EXTI 1.简介 2.AFIO&#xff1a;复用功能IO&#xff0c;主要用于重映射和外部中断映射配置​编辑 3. 中断使用 4.HAL库配置使用 一、中断简介 中断的意义&#xff1a;高效处理紧急程序&#xff0c;不会…

SPRD Android 14 通过属性控制系统设置显示双栏或者单栏

SPRD Android 14 通过属性控制系统设置显示双栏或者单栏 第一步 确认有添加静态库第二步 验证第三步 修改源码在合适的地方配置 ro.product.is_support_SettingsSplitEnabled 即可。第一步 确认有添加静态库 --- a/packages/apps/Settings/Android.bp +++ b/packages/apps/Set…

php反序列化逃逸

php反序列化逃逸 逃逸是php中反序列化时的恶意利用&#xff0c;以ctf为例演示 第一段演示 逃逸为ctf反序列化的内容&#xff0c;主要是对序列化对象进行过滤&#xff0c;其中替换串长度不一致&#xff0c;造成字符逃逸。攻击者可以构造恶意的payload&#xff0c;改变对象中的…

MATLAB循环语句

MATLAB 循环语句 在某些情况下&#xff0c;您需要多次执行一个代码块。通常&#xff0c;语句是按顺序执行的。首先执行函数中的第一条语句&#xff0c;然后执行第二条&#xff0c;依此类推。 编程语言提供了各种控制结构&#xff0c;允许更复杂的执行路径。 循环语句允许我们…

猫头虎分享已解决Bug || **Error: ‘Promise‘ is undefined**

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

3.8设计模式——State 状态模式(行为型)

意图 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。 结构 Context&#xff08;上下文&#xff09;定义客户感兴趣的接口&#xff1b;维护一个ConcreteState子类的实例&#xff0c;这个实例定义当前状态。State&#xff08;状态&#xff09;定义…

4 -25

1 100个英语单词两篇六级阅读 2 cf补题&#xff1b; 3 仿b站项目看源码 debug分析业务。 上了一天课&#xff0c;晚上去健身。 物理备课&#xff0c;周六去上课腻。 五一回来毛泽东思想期末考试&#xff0c;概率论期中考试。

冯诺依曼体系结构再谈操作系统

前言 前面对基本的指令、权限以及环境开发的基本工具进行了介绍&#xff0c;本期开始我们将进入系统的的学习&#xff0c;例如我们常听的进程等&#xff0c;在进入进程的学习前我们先要铺垫一下&#xff0c;所以本期我们先来介绍一下冯诺依曼体系结构和再谈操作系统&#xff0…