智能指针循环引用——你真的懂了吗?

news2024/11/16 2:44:32

相信不少同学都在面试中都被问到过c++智能指针的问题,接踵而至的必定是循环引用了,而我每次的答案都是一招鲜:因为它们都在互相等待对方先释放,所以造成内存泄漏。面试官很满意,我也很满意。

但是为啥要等到对方先释放?在我内心也曾有个问号。。

智能指针起源

所谓人类进步的阶梯就是懒,曾几何时,就有人想,我把new 运算符返回的指针p交给一个对象“托管”,而不用操心在哪里去释放这个指针p,由这个托管者自动的在合适的时机进行指针p的释放,这样也不怕自己忘记释放指针p了。而且,我这边操作托管者,要跟操作原来的指针一毛一样,这样才方便。即假设托管指针p的对象叫做shared_ptr,那么

*shared_ptr 所指,便是 *p 所向!

第一章 auto_ptr

最早的智能指针类,实现了*运算符。你用它实例化出来一个类对象,用起来就好像指针一样。对象的特性自然是在作用域结束时,自动调用析构函数实现自我销毁。如此一来,攻城狮们便可不再操心何时释放指针,而可以随心所欲的摸鱼啦。

但其有个致命缺陷:因为其缺省的复制和赋值构造函数,带来的同一个对象被多个智能指针托管,释放的时候便混乱异常,一不小心就重复释放了,使得用户使用起来战战兢兢,老早就被淘汰了。

第二章 unique_ptr

为了规避auto_ptr可以随意复制和赋值构造的缺陷,就推出了unique_ptr, 其实就是给auto_ptr简单换了个名字,并禁用掉其默认的复制和赋值构造函数,好嘛,大家都别用了<(`^′)>,不写代码,就不会有bug!

第三章 shared_ptr

只有一个unique_ptr,对于省吃俭用的高级攻城狮们来说自然是不够的,于是就有人站了出来喊了一声:我们要搞共享经济——砰!基于引用计数的shared_ptr横空出世了,每多一个人持有这个对象,引用计数就加一;每少一个人持有这个对象,引用计数就减一。引用计数为零时,才做真正的销毁操作。

很快,梦魇悄然而至,“我的shared_ptr怎么没有释放?”——一位焦头烂额的格子少年路过。。

起初,没有人在意这场灾难,这不过是一个指针的丢失、一个bug,一个服务器的宕机,直到这场灾难和每个人息息相关......

"你看这个人写的代码,它好像一坨狗屎"

#include <iostream>

class B;
class A {
public:
    A() {
        std::cout << "A" << std::endl;
    }
    ~A() {
        std::cout << "~A" << std::endl;
    }
public:
	std::shared_ptr<B> ptr;
};

class B {
public:
    B() {
        std::cout << "B" << std::endl;
    }
    ~B() {
        std::cout << "~B" << std::endl;
    }
public:
	std::shared_ptr<A> ptr;
};

void fun() {
	std::shared_ptr<A> pa(new A());
	std::shared_ptr<B> pb(new B());
	pa->ptr = pb;
	pb->ptr = pa;
}

int main()
{
	fun();
	return 0;
}

运行结果我不敢看: (*/ω\*)

纳尼?A和B的析构函数都没有被调用,妥妥的内存泄漏了! 

事后,据某位亲身经历这次事件的大牛回忆说:“喔,当时的内存布局是这个样子的!”

“对象A同时被pa和对象B中的ptr两个智能指针托管,所以引用计数为2;对象B同时被pb和对象
A中的ptr两个智能指针托管,所以引用计数也为2。那么当fun函数执行完,栈对象pb、pa依次开始执行这样的析构函数:”

// 大牛随手写的析构函数伪代码
// Copyright 2022 DaNiu. All rights reserved.

if (--ref_cnt == 0) delete obj;

“紧接着内存布局就变成了这样:”

“pa和pb已经销毁了,然而对象A和B,已经迷失在这浩瀚内存中,亘古难灭。。。吾称之为循环引用!”

路人震惊:“嘶!随着这样的迷失越来越多,这片天地再也无人可搞对象!!!”

゜゜(´O`) ゜゜。:“不要啊!我还没有搞过对象呢。”

......

庆幸的是,不就之后,有人就在shared_ptr出世的那方世界的一个不起眼的角落里,发现了一个小家伙,伴shared_ptr而生。

终章 weak_ptr

为了解决shared_ptr在在循环引用中存在的资源泄漏问题,weak_ptr在这种场景下应用而生,weak_ptr指向的智能指针对象,其引用计数不会加一,也就不会存在无法释放的问题了。

解决的方法就是,把A和B其中的一个ptr改成weak_ptr。

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

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

相关文章

STM8开发实例-ADC

ADC 1、ADC介绍 ADC 是任何现代微控制器中非常重要的外设。 它用于读取传感器的模拟输出、检测电压电平等。 例如,我们可以使用 ADC 读取 LM35 温度传感器。 传感器的电压输出与温度成正比,因此我们可以使用电压信息来反算温度。 下图是STM8s的ADC外设框图: 在使用 ADC 之…

猿如意|初识CSDN的开发者工具合集

前言&#xff1a; CSDN网站其实不仅仅有博客&#xff0c;虽然整个网站是基于博客开始的&#xff0c;但无疑博客是整个网站的魂。 那么&#xff0c;现在的CSDN还有第二个和第三个魂&#xff0c;就是云计算服务和猿如意了。 猿如意好像是CSDN5 6月份推出的&#xff0c;具体时间…

CSC7715 同步整流

CSC7715是一款用于开关电源的高效率同步整流控制IC。其具备较高的集成度&#xff0c;在有效的提升开关电源的转换效率的同时&#xff0c;减少了外围元器件的应用。CSC7715可用于DCM/QR开关电源系统。该电路内置45V的功率管&#xff0c;在系统中替代次级肖特基管,并提高整个系统…

Typora+PicGo+阿里云OSS

配置Typora 文章目录配置Typora阿里云1&#xff09;网页搜索阿里云OSS2&#xff09;注册账号3&#xff09;点击立刻开通a) 点击“产品价格”b) 初次付费c) 交钱以免造成后续无法访问d&#xff09;进入管理控制台e) 创建钥匙PicGo1&#xff09;下载安装2&#xff09;设置选择显示…

Pandas1.5.2 学习心得

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、pandas是什么&#xff1f; 二、安装 1.pip install pandas 2.Series(系列&#xff09; 可以通过索引标签获取和设置值 总结 前言 提示&#xff1a;以下是…

深度学习笔记

动手深度学习v2 引言 机器学习中的关键组件 无论什么类型的机器学习&#xff0c;都需要以下组件&#xff1a; 学习的数据转换数据的模型目标函数&#xff0c;量化模型的有效性调整模型参数以优化目标函数的算法 数据 大多时候遵循独立同分布&#xff08;指随机过程中&…

Java容器源码重点回顾——LinkedList

1. 概述 public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.SerializableLinkedList是实现了List和Deque接口的双端链表。LinkedList的底层数据结构是链表&#xff0c;不支持随机读取&a…

java TCP接收数据

查看本文 你需要先了解 TCP发送数据 如果没有了解过 可以查看我的文章 java TCP发送数据 然后 我们创建一个包 包下创建两个类 sendOut 客户端类 参考代码如下 import java.io.IOException; import java.io.OutputStream; import java.net.Socket;public class sendOut {publ…

13-Java方法

目录 1.方法的基本用法 1.1.什么是方法 1.2.方法定义语法 1.3.方法调用的执行过程 1.4.实参和形参的关系 1.5.方法的返回值 2.方法重载 2.1.方法重载定义 2.2.代码示例 3.方法递归 3.1.方法递归定义 3.2.方法递归使用条件 3.3.递归与非递归优劣比较 3.4.递归执行…

磷脂PEG衍生物Biotin-PEG-DSPE,生物素PEG磷脂,CAS:385437-57-0

英文名称&#xff1a;Biotin-PEG-DSPE、DSPE-PEG-Biotin 中文名称&#xff1a;生物素-聚乙二醇-磷脂 Item no&#xff1a;X-GF-0068-10k Classification&#xff1a;Biotin PEG DSPE PEG CAS&#xff1a;385437-57-0 MV&#xff1a;可定制&#xff0c;2000、1000、3400、1…

241. 楼兰图腾——树状数组

在完成了分配任务之后&#xff0c;西部 314 来到了楼兰古城的西部。 相传很久以前这片土地上(比楼兰古城还早)生活着两个部落&#xff0c;一个部落崇拜尖刀(V)&#xff0c;一个部落崇拜铁锹(∧)&#xff0c;他们分别用 V 和 ∧ 的形状来代表各自部落的图腾。 西部 314 在楼兰…

xv6 makefile详解

文章目录makefile语法格式生成qemu可执行文件生成kernel可执行文件生成kernel下的OBJSkernel.ldbuild OBJS_KCSANbuild initcode生成一个fs.img文件系统mkfs用户程序的编译配置工具makefile语法格式 makefile就是一个深搜的过程&#xff0c;最上面的语句是顶级目标&#xff0c…

python-函数、文件、异常、模块

目录 函数 返回值 函数传参 位置参数 关键字传参 缺省参数 不定长参数 匿名函数 文件操作 open 函数 异常 模块 导入模块 函数 返回值 return语句[表达式]退出函数&#xff0c;选择性地向调用方返回一个表达式。不带参数值的return语句返回None #定义函数 def a…

Ubuntu系统装机流程(显卡驱动、cuda、cudnn、搜狗输入法、anaconda、pycharm)

整体流程一、安装Ubuntu18.04系统二、安装显卡驱动三、安装Cuda四、安装Cudnn五、安装搜狗输入法六、安装Anaconda七、安装Pycharm社区版一、安装Ubuntu18.04系统 &#xff08;1&#xff09;实现用软碟通做好一个装有Ubuntu18.04的系统盘。 &#xff08;2&#xff09;打开电脑…

Jenkins自动发布到Docker部署服务器把Jar包打包成镜像并启动容器

《jenkins自动化发布到服务器并自动运行》 第1种方法&#xff1a;使用外部Jar包完成自动化部署&#xff08;简单方便&#xff09;&#xff0c;正式环境更新jar包时&#xff0c;备份一下旧的的jar包即可。 修改jenkins项目配置 Pre Steps 构建前清除旧的jar包&#xff0c;然后…

计算机网络原理第2章 物理层

目录 2.1 物理层的基本概念 2.2.1 数据通信系统的模型 2.2.2 有关信号的几个基本概念 1.通信 2.调制 3.编码 2.2.3 信道的极限容量 1.信道能够通过的频率范围&#xff08;奈氏准则&#xff09; 2. 信噪比&#xff08;香农公式&#xff09; 3.奈氏准则与香农公式的比…

Linux操作系统CentOS7安装mysql5.7.x

一、下载mysql5.7.x安装包 &#x1f308; MySQL官方下载&#xff1a;https://dev.mysql.com/downloads/mysql/5.7.html 注意&#xff0c;需要在Windows上解压之后&#xff0c;会有两个压缩包&#xff0c;将其中一个上传 二、将mysql5.7.x安装包上传到Linux服务器 使用 Xftp 上传…

NeurIPS 2022 Spotlight | SNAKE:首个同时进行隐式重建和三维特征点提取的方法

原文链接&#xff1a;https://www.techbeat.net/article-info?id4361 作者&#xff1a;钟程亮 3D特征点检测在物体识别、场景重建等任务中有着重要作用。然而由于点云数据采样的稀疏性&#xff0c;从中检测出3D特征点是一项很有挑战性的任务。虽然原始点云的获取方式有很多种&…

Kafka基础_1

Kafka系列 注&#xff1a;大家觉得博客好的话&#xff0c;别忘了点赞收藏呀&#xff0c;本人每周都会更新关于人工智能和大数据相关的内容&#xff0c;内容多为原创&#xff0c;Python Java Scala SQL 代码&#xff0c;CV NLP 推荐系统等&#xff0c;Spark Flink Kafka Hbase …

一文讲懂泛型

Java高级Java高级语言特性一. 泛型1. 1 为什么我们需要泛型1. 2 泛型类和泛型接口的定义1. 3 泛型方法1. 4 限定类型变量1. 5 泛型中的约束和局限性1. 6 泛型中的继承规则1. 7 通配符类型1.7.1 问题抛出&#xff0c;为啥需要通配符&#xff1f;1.7.2 &#xff1f; extends X1.7…