【C++11】weak_ptr智能指针使用详解

系列文章目录

【C++11】智能指针与动态内存


文章目录

  • 系列文章目录
    • 简介
    • 一、头文件
    • 二、初始化及使用
      • 1. 使用一个shared_ptr来初始化
    • 三、循环引用
      • 3.1 循环引用
      • 3.2 循环引用 解决方法


简介

在C++编程中,处理循环引用是一个常见的问题。循环引用可能导致内存泄漏和资源管理问题。为了解决这个问题,C++11引入了weak_ptr智能指针。

弱指针(weak_ptr)是一种不受控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。允许你共享对象的所有权,但不会增加对象的引用计数。它是一种弱引用,不会阻止对象的销毁。一旦一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也会被释放。


一、头文件

本文出现的关于unique_ptr的方法都包含在此头文件中

#include <memory>

二、初始化及使用

1. 使用一个shared_ptr来初始化

用一个shared_ptr来初始化,不会改变shared_ptr的引用计数

auto p = std::make_shared<int>(4);   
std::weak_ptr<int> wp(p);  

由于wp指向的对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock。

此函数检查weak_ptr指向的对象是否存在。如果存在,lock返回一个指针指向共享对象的shared_ptr;否则返回一个空的shared_ptr。

if (std::shared_ptr<int> np = wp.lock())  // 如果np不为空则条件成立
{// np与p共享对象
}

三、循环引用

3.1 循环引用

环引用是指两个或多个智能指针相互引用,形成一个闭环,导致它们都无法被释放,从而造成内存泄漏。循环引用通常发生在两个或多个对象之间存在相互依赖关系时,它们需要同时被释放。

下述代码就是循环引用的典型例子,a和b所指向的对象的引用计数永远不会为0,也就不会释放对象。

class B;
class A
{
public:std::shared_ptr<B> b_ptr;A() {std::cout << "A Constructor Called" << std::endl;};~A() {std::cout << "A Desstructor Called" << std::endl;};
};class B
{
public:std::shared_ptr<A> a_ptr;  //这里改成weak_ptr即可解决循环引用B() {std::cout << "B Constructor Called" << std::endl;};~B() {std::cout << "B Desstructor Called" << std::endl;};
};int main()
{ std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();std::cout << "a uses" << " " << a.use_count() << std::endl;   // 引用计数输出1std::cout << "b uses" << " " << b.use_count() << std::endl;   // 引用计数输出1a->b_ptr = b;b->a_ptr = a;std::cout << "a uses" << " " << a.use_count() << std::endl;   // 引用计数输出2std::cout << "b uses" << " " << b.use_count() << std::endl;   // 引用计数输出2std::cout << "Hello World!\n";
}

析构规定

在C++中,局部变量的析构顺序与它们的构造顺序相反。也就是说,最后构造的对象会首先被析构。这被称为栈解旋(stack unwinding)。

在C++中,当一个对象被销毁,其成员变量的析构函数会在该对象的析构函数之后被调用。也就是说,首先调用对象的析构函数,然后调用其成员变量的析构函数。确保了在对象的析构函数中,你仍然可以访问和操作其成员变量,因为它们此时还没有被销毁。

shared_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它所占用的内存。

上述代码执行完毕,析构函数的调用情况以及引用计数变化情况的说明

①a 和 b 离开作用域之前

a和b->a_ptr指向A的同一个实例对象,该对象的引用计数为2
b和a->b_ptr指向B的同一个实例对象,该对象的引用计数为2

②b调用析构函数

a和b离开作用域时,b先调用析构函数,这会导致b指向对象的引用计数减1,但是,由于a->b_ptr指向同一个对象,所以b指向对象的引用计数仍然为1。

③b->a_ptr调用析构函数

因为b指向对象的引用计数不为0,故其其析构函数没有被执行,对象没有被销毁。这导致其成员变量b->a_ptr的析构函数也不会被调用(对象本身没有被销毁的话,其成员变量的析构函数不会被调用),a指向对象的引用计数不变仍为2。

④a调用析构函数

a和b离开作用域时,b先调用析构函数,然后a调用析构函数,这会导致a指向对象的引用计数减1,但是,由于b->a_ptr也指向同一个对象,所以a指向对象的引用计数仍为1。

⑤a->b_ptr调用析构函数

因为a指向对象的引用计数不为0,故其析构函数没有被执行,对象没有被销毁。这导致其成员变量a->b_ptr的析构函数也不会被调用,b指向对象的引用计数不变仍为1。

最后对象a和b的引用计数都为1,不会销毁对象,释放内存,从而导致内存泄漏。

3.2 循环引用 解决方法

将B类中的shared_ptr改为weak_ptr,这样就可以打破循环引用,当a和b离开作用域时,它们的引用计数会变为0,析构函数会被调用,内存会被释放。

①a和b离开作用域之前

a指向A的一个实例对象,a指向对象的引用计数是1(b->a_ptr弱指针,引用计数不会加1)
b和a->b_ptr指向B的同一个实例对象b,对象b引用计数是2

②b调用析构函数

a和b离开作用域时,b先调用析构函数,这会导致b指向对象的引用计数减1,但是,由于a->b_ptr指向同一个对象,所以b指向对象的引用计数仍然为1。

③b->a_ptr调用析构函数

因为b指向对象的引用计数不为0,故其其析构函数没有被执行,对象没有被销毁。这导致其成员变量b->a_ptr的析构函数也不会被调用(对象本身没有被销毁的话,其成员变量的析构函数不会被调用),a指向对象的引用计数不变仍为1。

④a调用析构函数

a和b离开作用域时,b先调用析构函数,然后a调用析构函数,这会导致a指向对象的引用计数减1。但因为b->a_ptr是弱指针,不会增加引用计数,所以a指向对象的引用计数变为0,会执行析构函数,销毁对象,释放内存。

⑤a->b_ptr调用析构函数

因为a指向对象的引用计数为0,故其析构函数被执行,对象被销毁。从而其成员变量a->b_ptr的析构函数也会被调用,b指向对象的引用计数递减也变为0,销毁对象,释放内存。

最后对象a和b的引用计数都为0,不会导致内存泄漏


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

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

相关文章

镀膜与干刻中的平均自由程是什么?

在芯片制造中&#xff0c;镀膜和干刻是其中的重要环节&#xff0c;通常要用到CVD&#xff0c;RIE等技术&#xff0c;对材料表面进行纳米级的精细操作。在这些工序中&#xff0c;原子&#xff0c;分子&#xff0c;离子等&#xff0c;会在气体或真空中进行自由运动&#xff0c;直…

IDEA 高分辨率卡顿优化

VM设置优化 -Dsun.java2d.uiScale.enabledfalse 增加该条设置&#xff0c;关闭高分切换 https://intellij-support.jetbrains.com/hc/en-us/articles/115001260010-Troubleshooting-IDE-scaling-DPI-issues-on-Windows​intellij-support.jetbrains.com/hc/en-us/articles/1…

金融业务系统: Service Mesh用于安全微服务集成

随着云计算的不断演进&#xff0c;微服务架构变得日益复杂。为了有效地管理这种复杂性&#xff0c;人们开始采用服务网格。在本文中&#xff0c;我们将解释什么是Service Mesh&#xff0c;为什么它对现代云架构至关重要&#xff0c;以及它是如何解决开发人员今天面临的一些最紧…

py 字符串转INT

在Python中&#xff0c;可以使用内置的int()函数将字符串转换为整数&#xff08;INT&#xff09;。以下是一个简单的示例&#xff1a; s "123" i int(s) print(i) # 输出&#xff1a;123在上述代码中&#xff0c;我们首先定义了一个字符串s&#xff0c;它包含了一…

centos三台主机配置互信ssh登录

1. 修改hosts信息 1.1三台主机上分别修改hosts文件 vi /etc/hosts1.2 三台主机分别填入如下内容&#xff0c;ip地址需要检查正确 192.168.126.223 node1 192.168.126.224 node2 192.168.126.225 node32. 秘钥生成和分发 2.1 在三台主机上分别生成秘钥 命令输入后&#xff…

剑指JUC原理-19.线程安全集合

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

IDEA-git commit log 线

一、本地代码颜色标识 红色&#xff1a;新建的文件&#xff0c;没有add到git本地仓库蓝色&#xff1a;修改的文件&#xff0c;没有提交到git远程仓库绿色&#xff1a;已添加到git本地仓库&#xff0c;没有提交到git远程仓库灰色&#xff1a;删除的文件&#xff0c;没有提交到g…

苍穹外卖--实现公共字段自动填充

也就是在插入或者更新的时候为指定字段赋予指定的值&#xff0c;使用它的好处就是可以统一对这些字段进行处理&#xff0c;避免了重复代码。在上述的问题分析中&#xff0c;我们提到有四个公共字段&#xff0c;需要在新增/更新中进行赋值操作。 实现步骤&#xff1a;* 1). 自定…

Redis 19 事务

Redis通过MULTI、EXEC、WATCH等命令来实现事务&#xff08;transaction&#xff09;功能。事务提供了一种将多个命令请求打包&#xff0c;然后一次性、按顺序地执行多个命令的机制&#xff0c;并且在事务执行期间&#xff0c;服务器不会中断事务而改去执行其他客户端的命令请求…

curl网络请求命令

curl简介 1、什么是curl2、curl命令的基本使用 1、什么是curl CURL&#xff08;CommandLine Uniform Resource Locator&#xff09;是一个利用URL语法&#xff0c;在命令行终端下使用的网络请求工具&#xff0c;支持HTTP、HTTPS、FTP等协议 Linux、MAC系统一般默认已安装好CUR…

169. 多数元素 --力扣 --JAVA

题目 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 解题思路 利用Arrays自带的函数对数组进行排序&#xff1b;记录起始位置和…

QT专栏1 -Qt安装教程

#本文时间2023年11月18日&#xff0c;Qt 6.6# Qt 安装简要说明&#xff1a; Qt有两个版本一个是商业版本&#xff08;收费&#xff09;&#xff0c;另一个是开源版本&#xff08;免费&#xff09;&#xff1b; 打开安装程序时&#xff0c;通过判断账号是否有公司&#xff0c;安…

基于SSM的学生疫情信息管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

100套Axure RP大数据可视化大屏模板及通用组件库

106套Axure RP大数据可视化大屏模板包括了多种实用美观的可视化组件库及行业模板库&#xff0c;行业模板涵盖&#xff1a;金融、教育、医疗、政府、交通、制造等多个行业提供设计参考。 随着大数据的发展&#xff0c;可视化大屏在各行各业得到越来越广泛的应用。可视化大屏不再…

高斯积分-Gaussian Quadrature

https://mathworld.wolfram.com/GaussianQuadrature.html

mmdet 3.x 打印各类指标

和mmdet2.x中的修改地方不一样&#xff0c;在mmdet/evaluation/metrics/coco_metric.py中第72行将classwise设为True就可以打印各类指标了 但是在test的时候一直都是什么指标都不打印&#xff0c;不管是上面总的指标还是下面的各类指标&#xff0c;暂时不知道怎么处理 找到原因…

解决docker运行elastic服务端启动不成功

现象&#xff1a; 然后查看docker日志&#xff0c;发现有vm.max_map_count报错 ERROR: [1] bootstrap checks failed [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] 解决办法&#xff1a; 1. 宿主机&#xff08;运行doc…

移远EC600U-CN开发板 11.15

制作一个简单UI: 1."端口设置"模块 *效果图 *代码 def backEvent(evt): #返回主界面code evt.get_code() if code lv.EVENT.CLICKED:lv.scr_load(mainInterface)def popUpEvent(evt): #弹窗提醒code evt.get_code()if code lv.EVENT.CL…

Azure 机器学习:使用 Azure 机器学习 CLI、SDK 和 REST API 训练模型

目录 环境准备克隆示例存储库 示例案例在云中训练1.连接到工作区PythonAzure CLIREST API 2. 创建用于训练的计算资源4. 提交训练作业PythonAzure CLIREST API 注册已训练的模型PythonAzure CLIREST API Azure 机器学习提供了多种提交 ML 训练作业的方法。 在本文中&#xff0c…

Nuxt3框架局部文件引用外部JS/CSS文件的相关配置方法

引入外部JS&#xff1a; <script setup>useHead({script: [ {type: "text/javascript",src: https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min.js}]}) </script>useHead只能与组件的setup和生命周期钩子一起使用 如果需要将js放置body区…