c++的智能指针(5) -- weak_ptr

news2024/9/30 1:29:54

概述

  • 我们在使用shared_ptr会出现以下的问题,会导致内存泄露。

代码1:  类内指针循环指向

#include <iostream>
#include <memory>

class B;

class A {
public:
	A() {
		std::cout << "Construct" << std::endl;
	}
	~A() {
		std::cout << "Destruct" << std::endl;
	}

	void setPtr(std::shared_ptr<B> b) {
		this->p1 = b;
	}
private:
	std::shared_ptr<B> p1;
};

class B {
public:
	B() {
		std::cout << "Construct" << std::endl;
	}
	~B() {
		std::cout << "Destruct" << std::endl;
	}

	void setPtr(std::shared_ptr<A> a) {
		this->p1 = a;
	}
private:
	std::shared_ptr<A> p1;
};

int main(void) {

	{
	std::shared_ptr<A> pa(new A());
	std::shared_ptr<B> pb(new B());

	pa->setPtr(pb);
	pb->setPtr(pa);
    }

	std::cin.get();
}

结果: 

 

问题:

我们代码中,定义了两个类,main中定义了两个指针指向,分别指向两个类的动态空间,从输出结果会发现,我们pa和pb指向的动态空间,在pa和pb释放的时候并没有释放掉。 
这很显然不符合我们使用智能指针的预期。


因为我们使用智能指针,指向动态开辟的空间,它在自己对象释放的时候并没有释放对应的动态空间。--  这样就会导致内存泄露

原因:

原因是什么呢?  
就是我们的A,B类中都存在有指向对方的智能指针,当我们在main中定义指向类A和类B动态空间的智能指针的时候,同时,我们使用setPtr函数,将其内部的智能指针进行了赋值,分别指向了对方。(A内的智能指针指向B的动态空间,B内的智能指针指向A的动态空间)

这时候,对于动态开辟的A类和B类的动态空间而言,都同时存在着两个智能指针管理(一个是main中定义的,一个是类中的成员),引用计数为2。


这时候,当main中定义的智能指针析构,指向两块动态空间的引用计数-1,但是因为之前引用计数为2,-1之后为1,不是0,所以其不会释放动态开辟的空间。


因为main中指向两块空间的指针已经析构,我们已经无法管理这两块空间,但是它们却没有被释放,所以就造成了内存的泄露

 如图:

 

 

解决方法:  

方法一:  在类内部将内部指针设置为空 
class A {
public:
	A() {
		std::cout << "Construct" << std::endl;
	}
	~A() {
		std::cout << "Destruct" << std::endl;
	}

	void setPtr(std::shared_ptr<B> b) {
		this->p1 = b;
	}

	void deletePtr() {
		p1 = nullptr;
	}
private:
	std::shared_ptr<B> p1;
};

class B {
public:
	B() {
		std::cout << "Construct" << std::endl;
	}
	~B() {
		std::cout << "Destruct" << std::endl;
	}

	void setPtr(std::shared_ptr<A> a) {
		this->p1 = a;
	}

	void deletePtr() {
		p1 = nullptr;
	}
private:
	std::shared_ptr<A> p1;
};

int main(void) {

	{
		std::shared_ptr<A> pa(new A());
		std::shared_ptr<B> pb(new B());

		pa->setPtr(pb);
		pb->setPtr(pa);

		pa->deletePtr();
		pb->deletePtr();
    }

	std::cin.get();
}

我们在类的内部添加了deletePtr()函数,主要用来将类中的智能指针设置为空,我们只需要在main中的智能指针析构的时候调用对应类中的deletePtr()函数就可以做到释放动态开辟的空间了。

但是,我们使用智能指针的目的是为了让其帮助我们管理动态空间,如果使用这种方式,就失去了其意义,显然这种方式并方便(因为需要我们自己去调用函数,才能成功释放空间)。 

 

方式二: 使用weak_ptr智能指针 
int main(void) {

	{
		std::shared_ptr<A> pa(new A());
		std::shared_ptr<B> pb(new B());

		pa->setPtr(pb);
		//pb->setPtr(pa);
    }

	std::cin.get();
}

我们对main中的代码稍作修改,就是将pb调用setPtr()函数给注释掉。
也就是,我们不让类B中的智能指针指向类A的动态空间,这样类A的动态空间的引用计数就为1,在指向类A空间的智能指针pa释放的时候,引用计数减为0,这样类A的动态空间就会被释放。

类A的空间释放之后,那其内部的指向类B动态空间的智能指针也被释放,引用计数-1,然后智能指针pb在main中也被释放,这样这块空间的引用计数为0,也就被释放了。

 

如图:

 

weak_ptr 

鉴于上面这种思路,c++11有提出了weak_ptr,用来解决shared_ptr这种循环引用的问题

weak_ptr 

  • weak_ptr是一个智能指针,是用于保存shared_ptr的非拥有引用(弱引用),其不能直接访问指向的对象,必须转换成shared_ptr才可以访问。
  • weak_ptr在构建的时候,不能让它单独指向一个空间,必须使用shared_ptr的对象来构造它(也就是其指向shared_ptr指向的空间)。

    当然也可以将weak_ptr对象初始化为空,也可以使用空的weak_ptr来初始化它。

    也可以使用别的weak_ptr对象来构造其对象。
  • weak_ptr允许使用shared_ptr的对象赋值给它,但也只能这样赋值。(NULL或者nullptr也不能复制给它)

std::shared_ptr<int> p = std::make_shared<int>(5);
std::weak_ptr<int> w;
w = p;

w = NULL; // error

代码: 

 

 

为什么使用weak_ptr不会造成上面所提到的问题
  • weak_ptr指向与shared_ptr相同的空间,并不会增加其引用计数。

    std::shared_ptr<int> p1 = std::make_shared<int>();
    std::cout << "p1的引用计数" << p1.use_count() << std::endl;   // 输出: 1

    std::weak_ptr<int> w1(p1);
    std::cout << "w1的引用计数" << w1.use_count() << std::endl;   // 输出: 1

 会发现尽管w1和p1指向同一块空间,引用计数不变。

  • weak_ptr是用来辅助shared_ptr使用的,即使其指向一片空间,也仅仅是知道这块空间在哪个位置,不能访问空间中的数据。



我们使用w1访问其指向空间的数据,会发现出错了。

 

weak_ptr转换成shared_ptr (lock()函数)

我们要想使用weak_ptr对象访问数据等,必须将其转化为shared_ptr.

c++提供了lock()函数,可以帮助我们将weak_ptr转化成shared_ptr。 


lock()函数返回一个shared_ptr对象。如下,我们使用w1调用lock()函数,可以定义一个shared_ptr的对象来接收其返回值。

std::shared_ptr<int> p1 = std::make_shared<int>(5);
std::cout << *p1 << std::endl;  

std::weak_ptr<int> w1(p1);
std::shared_ptr<int> p2 = w1.lock();
std::cout << *p2 << std::endl;    // 这样就可以使用转换的共享指针访问数据。

 

代码2: 使用weak_ptr解决上面的问题 

#include <iostream>
#include <memory>

class B;

class A {
public:
	A() {
		std::cout << "Construct" << std::endl;
	}
	~A() {
		std::cout << "Destruct" << std::endl;
	}

	void setPtr(std::shared_ptr<B> b) {
		this->p1 = b;
	}
private:
	std::shared_ptr<B> p1;
};

class B {
public:
	B() {
		std::cout << "Construct" << std::endl;
	}
	~B() {
		std::cout << "Destruct" << std::endl;
	}

	void setPtr(std::shared_ptr<A> a) {
		this->p1 = a;
	}

private:
	std::weak_ptr<A> p1;
};

int main(void) {
	{
		std::shared_ptr<A> pa(new A());
		std::shared_ptr<B> pb(new B());

		pa->setPtr(pb);
		pb->setPtr(pa);
	}

	std::cin.get();
}

结果: 

 我们将类A和类B中的一个的智能指针修改为weak_ptr(代码中将类B修改),观察结果我们会发现成功析构了动态空间。

就是因为weak_ptr不会使得引用计数+1,所以类A的引用计数为1,main中析构pa之后,引用计数就变成了0,释放空间。

当我们需要使用类B中的智能指针访问其指向的空间的时候,我们就可以将其转化为shared_ptr然后再去访问。

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

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

相关文章

基于开源CrashRpt与微软开源Detours技术深度改造的异常捕获库分享

目录 1、异常捕获模块概述 2、为什么需要异常捕获模块&#xff1f; 3、在有些异常的场景下是没有生成dump文件的 4、开源异常捕获库CrashRpt介绍 5、对开源库CrashRpt的改进 C软件异常排查从入门到精通系列教程&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持…

# 从浅入深 学习 SpringCloud 微服务架构(二)模拟微服务环境(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;二&#xff09;模拟微服务环境&#xff08;1&#xff09; 段子手168 1、打开 idea 创建父工程 创建 artifactId 名为 spring_cloud_demo 的 maven 工程。 --> idea --> File --> New --> Project --> Ma…

基于贝叶斯算法的机器学习在自动驾驶路径规划中的应用实例

目录 第一章 引言 第二章 数据准备 第三章 贝叶斯路径规划模型训练 第四章 路径规划预测 第五章 路径执行 第六章 实验结果分析 第一章 引言 自动驾驶技术的发展带来了自动驾驶车辆的出现&#xff0c;而路径规划作为自动驾驶车辆的关键功能之一&#xff0c;对于确定最佳行…

锐捷校园网自助服务系统 operatorReportorRoamService SQL注入漏洞致RCE漏洞复现

0x01 产品简介 锐捷校园网自助服务系统是锐捷网络推出的一款面向学校和校园网络管理的解决方案。该系统旨在提供便捷的网络自助服务,使学生、教职员工和网络管理员能够更好地管理和利用校园网络资源。 0x02 漏洞概述 锐捷校园网自助服务系统 operatorReportorRoamService 接…

STP学习的第一篇

1.STP的基本概念&#xff1a;根桥 &#xff08;1&#xff09;STP的主要作用之一是在整个交换网络中计算出一棵无环的“树”&#xff08;STP树&#xff09;。 &#xff08;2&#xff09;根桥是一个STP交换网络中的“树根”。 &#xff08;3&#xff09;STP开始工作后&#xf…

一、MinIO基本知识

MinIO基本知识 一、简介1.许可 二、部署1.Docker部署1.1 部署容器 1.2 MinIO页面访问1.3 创建Bucket![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6c8aa92975f146b691f1f36ce1033e7c.png) 三、Python-API1.安装包2.Bucket、Object概念3.Bucket-API4.MinIOClient-…

【Yolov系列】Yolov5学习(一)补充1.2:自适应锚框计算详解+代码注释

一、自适应锚框计算详解 自适应锚框计算的具体过程&#xff1a; ①获取数据集中所有目标的宽和高。 ②将每张图片中按照等比例缩放的方式到 resize 指定大小&#xff0c;这里保证宽高中的最大值符合指定大小。 ③将 bboxes 从相对坐标改成绝对坐标&#xff0c;这里…

余氯控制器的功能优势简介

余氯控制器是一款智能化的水质监测设备&#xff0c;它采用高精度AD转换和单片机微处理技术&#xff0c;能够完成余氯值的高精度测量。这款控制器具备时间显示、数据存储等基本功能。 高智能化设计&#xff1a;余氯控制器采用了高精度AD转换和单片机微处理技术&#xff0c;确保…

VisualGLM-6B的部署步骤

对于如下命令&#xff0c;你将完全删除环境和环境中的所有软件包 conda remove -n env_name --all 一、VisualGLM-6B环境安装 1、硬件配置 操作系统&#xff1a;Ubuntu_64&#xff08;ubuntu22.04.3&#xff09; GPU&#xff1a;4050 显存&#xff1a;16G 2、配置环境 建…

防水型RTU IP68防水遥测终端机

在工业物联网的领域中&#xff0c;防水型RTU(Remote Terminal Unit)具有不可或缺的重要性。作为工业设备的守护神&#xff0c;它在实现数据采集和传输、远程控制和预警告警的同时&#xff0c;还能保障设备免受水分侵害&#xff0c;确保系统稳定安全的运行。    计讯物联防水…

JDK 11下载、安装、配置

下载 到Oracle管网下载JDK 11&#xff0c;下载前需要登录&#xff0c;否则直接点下载会出现502 bad gateway。 下载页面链接 https://www.oracle.com/hk/java/technologies/downloads/#java11-windows 登录 有些人可能没有Oracle账号&#xff0c;注册也比较慢&#xff0c;有需…

2024_GAMES101作业环境配置Mac(intel)_VSCode_Clion

目录 VSCodeClionCMakeList.txt VSCode brew install cmake 更换下载源为阿里云下载 opencv&#xff0c;不然会很慢 cd "$(brew --repo)" git remote -v cd "$(brew --repo)" git remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git…

Python --- 基于Iris flower数据集的kNN分类实战

基于Iris flower数据集的kNN分类实战 Iris data set(鸢尾花数据集简介) 鸢尾花数据集共包含三种鸢尾花&#xff1a;Iris setosa, Iris virginica and Iris versicolor。 Iris setosa&#xff08;山鸢尾&#xff09; Iris virginica&#xff08;维吉尼亚鸢尾 &#xff09; Iris …

本地环境测试

1. 在 Anaconda Navigator 中&#xff0c;打开 Jupyter Notebook &#xff0c;在网页中&#xff0c;点击进入本地环境搭建中创 建的工作目录&#xff0c;点击右上角的 New- 》 Folder &#xff0c;将新出现的 Untitled Folder 选中&#xff0c;并使用左上角 的 Rename 按钮重…

C++ //练习 12.30 定义你自己版本的TextQuery和QueryResult类,并执行12.3.1节(第431页)中的runQueries函数。

C Primer&#xff08;第5版&#xff09; 练习 12.30 练习 12.30 定义你自己版本的TextQuery和QueryResult类&#xff0c;并执行12.3.1节&#xff08;第431页&#xff09;中的runQueries函数。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1…

{“errMsg“:“insertXWebCamera:fail appid privacy api banned“}

问题描述&#xff1a;微信小程序&#xff0c;在体验版本测试时&#xff0c;调用摄像头OK&#xff0c;没有任何问题&#xff0c;部署发布版本后&#xff0c;日志报错内容&#xff1a;{"errMsg":"insertXWebCamera:fail appid privacy api banned"}&#xff…

opencv人脸打马赛克

import cv2def FaceFind(imgPath: str) -> list:image cv2.imread(imgPath)gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml)# 返回人脸坐标列表faces face_cascade.detectMultiScale(gray, scal…

MultiCD工具:创建一个多引导Linux USB驱动器

众所周知&#xff0c;拥有一个可安装多个可用操作系统的 CD 或 USB 驱动器在各种情况下都非常有用。无论是为了快速测试或调试某些内容&#xff0c;还是只是重新安装笔记本电脑或 PC 的操作系统&#xff0c;这都可以为你节省大量时间。 在本文中&#xff0c;将介绍如何使用名为…

高度自定义工业自动化配置:钡铼IOy系列模块广泛应用各行业案例

钡铼IOy系列模块是当今工业自动化领域中备受瞩目的一种设备&#xff0c;其高度自定义的特性使其在各行各业都得到了广泛的应用。无论是在制造业、能源领域还是物流行业&#xff0c;钡铼IOy系列模块都发挥着重要的作用&#xff0c;为企业提高生产效率、降低成本、提升产品质量等…

每日更新的Socks5代理服务推荐

大家好&#xff01;我是一名对于IP代理知识非常熟悉的作者&#xff0c;今天我要为大家介绍的是每日更新的Socks5代理服务推荐。作为一个资深的网络爱好者&#xff0c;我深知在网络世界中保护个人隐私和确保数据安全的重要性。而Socks5代理服务则是一种既能够隐藏我们真实IP地址…