c++智能指针(raii)

news2024/9/24 17:14:50

目录

1.智能指针的作用

2.智能指针带来的问题与挑战

 3.三种不同的智能指针

4.auto_ptr

5.unique_ptr

6.shared_ptr

7.weak_ptr;相互引用

8.总结


1.智能指针的作用

以c++的异常处理为例看看throw catch用法。有时,一个用new开出的空间用完还没delete呢。突然throw一个错误,程序就跑没了,造成了内存泄漏。为了解决这个问题,引入了智能指针的概念,作用是为了让这块空间可以自动释放。智能指针实际就是用一个类来管理这块空间,利用析构函数来释放空间。同时利用重载-> = *来实现一般指针相同的功能。 


2.智能指针带来的问题与挑战

因为是用一个类来进行管理,那么拷贝怎么办?我们都知道普通指针是采用的浅拷贝的方式,智能指针也得有普通指针的功能啊。但是,若是简单的完成浅拷贝就必然会导致同一块空间多次释放的问题。看图:

ap1和ap2都指向了同一块资源,俩再一析构,就全完了。


 3.三种不同的智能指针

a.auto_ptr:直接转移管理权(摆烂做法,不推荐)。

b.unique_ptr:直接不让你拷贝,拷贝就报错。(太粗暴,没有解决问题,也不推荐)。

c.shared_ptr:用引用计数的方式解决了这个问题。(推荐,完美解决问题)。


4.auto_ptr

上菜:

#include<iostream>
#include<string>
#include<map>

using namespace std;
class A
{
public:
	~A()
	{
		cout << "~A true" << endl; //验证析构次数
	}
	int _a=0; //为了下面验证才搞成public的
};
namespace cpp
{
	template<class T>
	class auto_ptr
	{
	private:
		T* _ptr;
	public:
		auto_ptr(T* ptr=nullptr)
			:_ptr(ptr)
		{}
		auto_ptr(auto_ptr<T>& ap) //拷贝构造
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr; 
			//auto_ptr用了非常搞的方式来拷贝,直接将资源管理权给拷贝对象,我摆烂辣!
			//被拷贝的对象直接置空,防止2次析构。
		}

		~auto_ptr()
		{
			if (_ptr) //非空删除空间
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}
		auto_ptr<T>& operator=(auto_ptr<T>& ap)
		{
			if (&ap != this) //要考虑自身赋值的问题
			{
				//与拷贝构造不同的是,先得把自己释放了,才可以拿别人的,防止内存泄漏。
				if (_ptr)
				{
					delete _ptr;
				}
				_ptr = ap._ptr;
				ap._ptr = nullptr; 
			}
			return *this;
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}

	};
}

再来看看测试:

int main()
{
	//看看能不能和普通指针一样用
	cpp::auto_ptr<A> ap1 = new(A);
	ap1->_a++;
	cout << ap1->_a << endl;

	//看看拷贝多次析构解决了没
	cpp::auto_ptr<A> ap2(ap1);
	ap2->_a++;
	cout << ap2->_a << endl;
	
	
}

 要是再调用ap1就崩溃,这auto_ptr不太行。


5.unique_ptr

上菜:

template<class T>
	class unique_ptr
	{
	private:
		T* _ptr;
	public:
		unique_ptr(T* ptr)
			:_prt = ptr;
		{}
		unique_ptr(unique_ptr<T>& up)=delete; //用delete关键字不让你用
		unique_ptr<T>& operator=(unique_ptr<T>& up) = delete;
		~unique_ptr()
		{
			if (_ptr) //非空删除空间
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}	
	};

这都没啥好试的,直接拷贝用不了。


6.shared_ptr

这么巧妙的shared_ptr得好好说说,我们想到计数引用,估计都会先想到用一个静态(static)的count来计数。但这是搞不定的,每个类都搞一个,无法共享count。所以,我们采用一个共享的空间来存储count。

template<class T>
	class shared_ptr
	{
	private:
		T* _ptr;
		int* _pCount; //用一块共享空间计数
	public:
		shared_ptr(T* ptr=nullptr)
			:_ptr(ptr)
			,_pCount(new int(1)) //默认构造给1
		{}
		shared_ptr(shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
		{
			++(*_pCount); //拷贝构造完成记得++count
		}
		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			if (&sp != this) //首先保证不是自己拷贝自己。
			{
				if (--(*sp._pCount) == 0) //当*pCount == 1时,要释放空间。
				{
					delete sp._ptr;
					delete sp._pCount;
				}
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				++(*_pCount);
			}
			return *this;
		}
		~shared_ptr()
		{
			if ((--*_pCount) == 0)
			{
				delete _ptr;
				delete _pCount;
			}
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}
	};
}

看看测试:

int main()
{	
	cpp::shared_ptr<A> ap1 = new(A);
	ap1->_a++;
	cpp::shared_ptr<A> ap2(ap1);
	ap2->_a++;
	cpp::shared_ptr<A> ap3 = ap2;
	ap3->_a++;
	ap3 = ap3;

	cout << ap2->_a << endl; //和auto_ptr不同的是完全解决了拷贝问题。
	cout << ap3->_a << endl;

	
}

 完美,以后再也不用担心漏掉delete啦,用完自动就释放。


7.weak_ptr;相互引用

class Node
{
public:
	~Node()
	{
		cout << "~Node true" << endl; //验证析构次数
	}
	int _val = 0;
	shared_ptr<Node> _next = nullptr; //必须用智能指针才能对应上类型
	shared_ptr<Node> _prev = nullptr;
};

int main()
{	
	shared_ptr<Node>sp1 (new(Node));
	shared_ptr<Node>sp2 (new(Node));

	//形成了相互引用
	sp1->_next = sp2; 
	sp2->_prev = sp1;

    cout << sp1.use_count() << endl; //use_count表示引用计数的大小
	cout << sp2.use_count() << endl;
}

 我注释掉一行,结果没问题。

 我去掉注释

就析构不了。

这就是引用计数的缺点,如图:

 两边count都变成了1,就僵住了。左边整个空间想要释放需要右边空间的_prev释放;右边空间的_prev释放需要右边整个空间释放;右边整个空间想要释放需要左边空间的_next释放;左边空间的_next释放需要左边整个空间释放。走边整个空间的释放......(串上了,没完没了了,谁也释放不了)。

这时,我们需要用弱化指针weak_ptr,直接让_next和_prev无法改变空间的引用计数。

shared_ptr是可以接收weak_ptr的。

看图查看差别:

ok,问题解决,直接让他俩count都是1。 


8.总结

不得不说,这c++为了填上没垃圾回收器的坑,搞了一套这么复杂的智能指针出来。有价值的就是shared_ptr啦。智能指针也会带来问题(相互引用)又得用weak_ptr来解决。不过,学会这套机制对于我们理解内存管理有很大的帮助。

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

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

相关文章

[附源码]java毕业设计壹家吃货店网站

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【C语言】学数据结构前必学的结构体struct详细

佛祖说&#xff0c;他可以满足程序猿一个愿望。程序猿许愿有生之年写出一个没有bug的程序&#xff0c;然后他得到了永生。 目录 1、结构体的声明与定义 1.1结构体是什么&#xff1f; 1.2为什么要有结构&#xff1f; 1.3结构体的声明 1.4结构体成员类型 1.5结构体变量定义…

由CPU高负载引发内核探索之旅

导语&#xff1a;STGW&#xff08;腾讯云CLB&#xff09;在腾讯云和自研业务中承担多种网络协议接入、请求加速、流量转发等功能&#xff0c;有着业务数量庞大、接入形式多样、流量规模巨大的特点&#xff0c;给产研团队带来了各种挑战&#xff0c;经常要深入剖析各种疑难杂症。…

Win7纯净版系统镜像64位介绍

Win7系统是一款非常经典的系统&#xff0c;这里想为大家介绍的是Win7纯净版系统镜像64位&#xff0c;主要特点就是非常稳定&#xff0c;运行流畅、占用CPU和内存都非常少。系统非常纯净&#xff0c;使用此系统&#xff0c;可让你的电脑焕然一新&#xff01; 一、系统稳定 1、采…

科普读书会丨《被讨厌的勇气》:愤怒不是目的,是一种工具

Hello&#xff0c; 这里是壹脑云读书圈&#xff0c;我是领读人小美~ 《被讨厌的勇气》读书会目前已经进行了两期&#xff0c;成员们也共同探讨了其中第一夜和第二夜的内容。每个人都有被情绪困扰的时候&#xff0c;而阿德勒心理学告诉我们&#xff0c;即使是负面情绪也不可怕…

WebRTC 服务器搭建篇

First off All 服务器环境&#xff1a;采用的阿里云国内服务器&#xff0c;系统&#xff1a; Ubuntu 16.04 64位 。 各个服务所需要的编译环境图&#xff1a; 各个服务器对应所需编译平台 1.第一步&#xff0c;先更新下命令行工具&#xff0c;工欲善其身必先利其器&#xff…

推荐一款图表功能强大的可视化报表工具

企业信息化建设&#xff0c;大量的数据需要经过分析才能挖掘价值。因此数据的价值越来越受到大家的重视&#xff0c;大数据分析工具逐渐成为企业运营必不可少的辅助工具。俗话说工人要想做好事&#xff0c;首先要磨利工具&#xff0c;拥有一个好用的大数据分析工具尤为重要&…

numpy生成0和1数组方法、从已有数组生成新数组方法、生成固定范围内数组、生成随机数组、绘制指定均值和标准差正态分布图、均匀分布图绘制

一、生成0和1数组 np.ones(shape, dtype)&#xff1a;shape为要生成的数组的维度&#xff0c;dtype为数组内元素类型np.ones_like(a, dtype)&#xff1a;生成与a同维度的数组np.zeros(shape, dtype)np.zeros_like(a, dtype) 代码如下 one np.ones([3,4]) one --------------…

机器学习——支持向量机与集成学习

支持向量机与集成学习 文章目录支持向量机与集成学习支持向量机的基本原理线性可分支持向量常用核函数集成学习概述集成学习的两种方式集成学习的基本类型弱学习其合成方式AdaBoost算法训练过程简例一类按监督学习方式对数据进行二元分类的广义线性分类器 文章目录支持向量机与…

免费不限时长的语音转文字软件——Word365

适用场景 想将语音转化成文字。 这里的语音可以是实时输入&#xff0c;也可以是已有音、视频转换成文字。 后者的操作比前者多一步操作。 1.实时语音转文字 可以直接打开Word365&#xff0c;【开始】选项卡中的【听写】功能。 打开前修改一下设置&#xff0c;语言可以根据需…

nmap之nse脚本简单学习

nmap之nse脚本简单学习 环境&#xff1a;centos8 nmap安装 yum -y install nmap -- 版本 [rootqingchen /]# nmap -version Nmap version 7.70 ( https://nmap.org )脚本入门学习 cd /usr/share/nmap [rootqingchen nmap]# ls nmap.dtd nmap-mac-prefixes nmap-os-db …

300dpi等于多少分辨率?如何给图片修改分辨率大小?

​图片是我们在生活中经常需要接触使用到的东西&#xff0c;无论是工作中还是生活中都离不开图片&#xff0c;在使用图片时我们会接触到“图片分辨率”、“dpi”这个概念&#xff0c;那么到底什么是图片分辨率&#xff1f;300DPI等于多少分辨率&#xff1f;如何给图片修改分辨率…

Lidar和IMU(INS)外参标定----常用开源项目总结

写在前面&#xff1a;博主主要关注的是自动驾驶中Lidar和RTK组合导航设备的标定&#xff0c;大部分的开源项目都把其转化为Lidar和IMU的标定过程。 1. ETH的lidar_align (Github)A simple method for finding the extrinsic calibration between a 3D lidar and a 6-dof pose …

推特精准客户开发手册

你要在巷子里营造出热闹的气氛&#xff0c;人为把热度炒起来&#xff0c;虚假的繁荣是做给别人看的&#xff0c;是用来吸引别人而不是说你自己沉迷于此&#xff0c;而“虚假的繁荣”又是个怎么的虚法呢&#xff1f;它需要外界看起来是真的。 可是问题来了&#xff0c;我们都知…

NTP时钟系统为制造业信息化产业提供守时保障

随着科学技术的发展&#xff0c;工业信息化高速迈进&#xff0c;高精度的同步时钟系统显得尤为重要。利用网络同步时钟系统技术对各个设备之间进行时间统一&#xff0c;对制造业和信息化产业提高产能&#xff0c;让生产力更高效提供守时保障。NTP时钟系统是基于网络时间协议而衍…

你问我答 | 解决关于入托的8个疑问

很多新手家长对于送孩子入托有很多顾虑&#xff0c;这次我们通过“你问我答”让家长更了解托班的意义。 Q&#xff1a;不好好吃饭的小宝宝&#xff0c;适合入托吗&#xff1f; A&#xff1a;适合。吃饭是孩子生活能力培养的重要部分&#xff0c;大部分孩子在入托前&#xff0c…

C. Binary String(思维+贪心)

Problem - 1680C - Codeforces 给你一个由字符0和/或1组成的字符串s。 你必须从字符串的开头去除几个&#xff08;可能是零&#xff09;字符&#xff0c;然后从字符串的结尾去除几个&#xff08;可能是零&#xff09;字符。移除后&#xff0c;字符串可能会变成空的。删除的代价…

【跟学C++】C++STL标准模板库——算法详细整理(中)(Study18)

文章目录1、简介2、STL算法分类及常用函数2.1、变序算法(一)2.2.1 初始化算法(2个)2.2.2 修改算法(2个)2.2.3 复制算法(6个)2.2.4 删除算法(6个)3、总结 【说明】 大家好&#xff0c;本专栏主要是跟学C内容&#xff0c;自己学习了这位博主【 AI菌】的【C21天养成计划】&#x…

大学生静态HTML网页设计--公司官网首页

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 公司官网网站 | 企业官网 | 酒店官网 | 等网站的设计与制 HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&#xff1a;结构 CSS&#xf…

容器服务 ACK 结合 MSE Ingress,让集群入口流量管理更丰富、更容易

作者&#xff1a;扬少 随着云原生技术不断普及&#xff0c;越来越多的业务应用开始向云原生架构转变&#xff0c;借助容器管理平台 Kubernetes 的不可变基础设施、弹性扩缩容和高扩展性&#xff0c;助力业务迅速完成数字化转型。其中&#xff0c;集群入口流量管理方式在云原生…