C++--智能指针--1123

news2025/7/15 8:38:35

1.智能指针解决的问题

int div()
{
     int a, b;
     cin >> a >> b;
     if (b == 0)
     throw invalid_argument("除0错误");
     return a / b;
}
void Func()
{
// 1、如果p1这里new 抛异常会导致p1不会Delete而导致内存泄漏
// 2、如果p2这里new 抛异常会导致p1和p2都不会delete而导致内存泄漏
// 3、如果div调用这里又会抛异常会导致p1和p2都不会delete而导致内存泄漏
     int* p1 = new int;
     int* p2 = new int;
     cout << div() << endl;
     delete p1;
     delete p2;
}
int main()
{
 try
 {
 Func();
 }
 catch (exception& e)
 {
     cout << e.what() << endl;
 }
 return 0;
}

综上,我们需要一种可以自动回收内存空间的指针。如果我们把指针套在一种类内,类在析构时会带着指针申请的空间一起析构掉。

class A
{
public:
	~A()
	{
		cout << "~A()" << endl;
	}
//private:
	int _a1 = 0;
	int _a2 = 0;
};

2.智能指针的使用及原理

2.1RAII

RAII(Resource Acquisition Is Initialization)直译:在初始化的时候获取资源。即在对象构造的时候获取资源,在对象的生命周期之中,控制对资源的访问,最后在对象析构的时候释放资源。

好处:

  • 不用显示的释放资源(delete)。
  • 对象所需的资源,在对象生命周期之内始终有效。

 模拟实现1

template<class T>
class SmartPtr {
public:
	SmartPtr(T* ptr = nullptr)
		: _ptr(ptr)
	{}

	~SmartPtr()
	{
		if (_ptr)
		{
			cout << "Delete:" << _ptr << endl;
			delete _ptr;
		}
	}

private:
	T* _ptr;
};

 2.2 原理

上述还不能将其称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可 以通过->去访问所指空间中的内容,因此:模板类中还得需要将* 、->重载下,才可让其 像指针一样去使用。 

T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

 智能指针的思想比较简单,但如何拷贝构造是个问题

 

 这里程序崩溃了 原因依然是之前的老问题,对同一块空间进行了两次析构。(别试,崩溃了)

那我们要跟之前一样实现深拷贝吗?不能,因为我们要的就是浅拷贝,要的就是用新的指针来指向我这块资源。

下面我们来看一下C++是如何对这里进行处理。

2.3 auto_ptr

auto_ptr<int>sp2(sp1)

C98里面的大槽点,对拷贝构造的处理沿用了右值引用的资源转移。(可右值本来就是将亡值,给了就给了。左值可不是啊,如果我还需要对sp1进行访问,那就会报访问空指针的错误)。我们将auto_ptr模拟实现一下

namespace chy
{
	template<class T>
	class auto_ptr
	{
	public:
		//用指针来构造的
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}
		//C98中的auto_ptr对于拷贝的问题处理有问题,相当于右值的资源转移
		//会把ap里面的资源转移给构造出来的智能指针,而ap将不在管理这些资源(置空)
		//所以如果我们再次对与于ap进行解引用操作,将会出现访问空指针的问题
		auto_ptr(auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}
		auto_ptr<T>& operator=(const auto_ptr<T>& ap)
		{
			if (this != &ap)//如果不是自己给自己赋值
			{
				if (_ptr)//原本的资源不管了(删除)
				{
					cout << "Delete" << _ptr << endl;
					delete _ptr;
				}
				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
			return *this;
		}
		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "Delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;//  &(*_ptr)
		}
	private:
		T* _ptr;
	};

}

 拷贝是没问题的 但是不能对sp1再进行解引用操作了

 3. C++11的智能指针

 3.1 unique_ptr

遇到问题,逃避问题

不让拷贝

unique_ptr(const unique_ptr<T>& ap) = delete;
unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;

template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr=nullptr)
			:_ptr(ptr)
		{}
		unique_ptr(const unique_ptr<T>& ap) = delete;
		unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;

		~unique_ptr()
		{
			if (_ptr)
			{
				cout << "Delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

3.2 share_ptr

引用计数,每有一个指针指向我这块区域,引用计数++,当最后一个指向改位置的智能指针析构时,带着区域一起走。

我这个计数怎么设置?

{
    //..
private:
    static int count;
    //..
}
template<class T>
int shared_ptr<T>::_count = 0;

这样设计可以解决多个指针指向同一块空间的问题。可如果我有两块空间呢?这两块空间的引用计数是不是就变成了同一个了?

所以我们不能纳入静态成员变量,同时我们注意到,我们每指向一块空间,就需要一个单独的计数。那我们在指向空间的时候构造计数不就好了?

于是我们在构造函数中下笔

{   //...
	shared_ptr(T* ptr = nullptr)
		: _ptr(ptr)			
        , _pCount(new int(1))
	{}
private:
    T* _ptr;
    int _pCount;
}

整体实现

template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr)
			:_ptr(ptr)
			,_pCount(new int(1))
		{}
		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			, _pCount(sp._pCount)
		{
			//二者公用一个计数
			(*_pCount)++;
		}
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			//赋值是 两个已有对象进行赋值 要先对_ptr指向的地方的计数--
			if (_ptr == sp._ptr)//两个指向的是同一块 区域 不用赋值
			{
				return *this;
			}
			if (--(*_pCount) == 0)
			{
				delete _ptr;
				delete _pCount;
			}
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			++(*_pCount);
			return *this;
		}
		~shared_ptr()
		{
			if (--(*_pCount) == 0)
			{
				cout << "Delete:" << _ptr << endl;
				delete _ptr;
				delete _pCount;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _pCount;
	};

 3.2.2 shared_ptr的循环引用问题

 

 函数结束时,n2先析构,然后n1析构。

_next管着右边的节点。 _prev管着左边的节点

_next析构,右边节点就delete。 _prev析构,左边节点就delete

那_next什么时候析构呢?左边的节点被delete,调用析构函数,其成员函数_next就析构了。

那左边的节点什么时候被delete呢? _prev析构的时候,左边的节点析构。

那_prev什么时候析构呢?右边的节点被delete,调用析构函数,其成员函数_prev就析构了。

那左边的节点什么时候被delete呢?........(开始套娃)

3.3 weak_ptr

weak_ptr不是常规的智能指针,没有RAII,不支持直接管理资源。weak_ptr主要用shared_ptr构造,用来解决shared_ptr循环引用问题。

(未完)

 

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

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

相关文章

【数据结构学习笔记】18:线段树(单点修改)

1 线段树上的操作 push_up(int u)&#xff1a;由子节点的信息去计算父节点的信息&#xff0c;例如两个子节点的区间和&#xff0c;加起来就是父节点表示的区间和。其中u是当前节点编号&#xff0c;表示用u的左右两个子节点来算一下自己这个节点的信息。push_down&#xff1a;将…

流氓设备检测和预防

自带设备 &#xff08;BYOD&#xff09; 策略中涉及的设备以及这些设备连接到的端口具有多个通信路径。确保这些设备及其路径在进入组织网络时立即被检测、评估和管理至关重要&#xff0c;因为非托管设备很容易成为安全风险。但是&#xff0c;在整个企业网络中同时添加许多设备…

反射、枚举、lambda——小记

文章目录反射反射定义反射相关的类Class 类反射示例获得Class对象的三种方式反射使用 ——代码面试题:你知道有几种创建对象的方式吗?反射优点和缺点枚举Lambda表达式概念Lambda表达式的语法代码反射 反射定义 Java的反射&#xff08;reflection&#xff09;机制是在运行状态…

路由策略和路由控制

路由策略和路由控制 路由策略 针对路由的发布&#xff0c;接收&#xff0c;引入进行控制&#xff0c;从而影响数据的路径或者可达性 路由匹配工具 ACL&#xff1a;访问前缀列表 一个ACL用多条规则组成&#xff0c;不同规则之间通过rule id进行区分&#xff0c;默认rule 步…

(附源码)python办公数据分析系统 毕业设计 021836

Python办公数据分析系统 摘 要 现代办公通过办公自动化系统可以大大提高的效率、节省成本、规范业务和流程&#xff0c;辅助提升管理水平。办公系统在单位信息化中占有非常重要的地位&#xff0c;涉及到单位的各个部门及绝大多数人员&#xff0c;流程和协作方面要求非常强。 办…

[附源码]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…

NDK 是什么 | FFmpeg 5.0 编译 so 库

前言 NDK 全称 Native Development Kit&#xff0c;也就是原生开发工具包 &#xff0c;官网对它有详细的 中文介绍 。可能一说到 NDK 或 JNI &#xff0c;大家脑子里第一反应就是集成 C/C 。其实 JNI 的含义是 Java Native Interface &#xff0c;这种接口允许 Java 和其他语言…

SpringBoot SpringBoot 原理篇 1 自动配置 1.3 bean 的加载方式【三】

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇1 自动配置1.3 bean 的加载方式【三】1.3.1 第三种方式1 自动配置 1.3 bean …

体系结构26_输入输出系统(3)

盘阵列&#xff08;RAID&#xff09; 盘阵列容量大、速度快、可靠性高、造价低廉。它是目前解决计算机I/O瓶颈的有效方法之一&#xff0c;有着广阔的发展前景。 盘阵列有多种组织方式&#xff1a; RAID 0 亦称数据分块&#xff08;Striping&#xff09;&#xff0c;即把数据分…

推特群推掀开营销新篇章

与Facebook和Instagram相比&#xff0c;Twitter营销并不是一个非常热门的营销渠道&#xff0c;对于跨境卖家来说可能会有一些陌生和挑战&#xff0c;但是作为一个重要的营销渠道&#xff0c;Twitter在全球市场上拥有超过1.45亿的日活跃用户(超过3.26亿的月活跃用户)&#xff0c…

Pinia基本使用

文章目录1. 介绍2. Pinia 和 Vuex3. 安装和基本使用4. pinia修改数据状态5. pinia持久化处理6. 自定义插件1. 介绍 它是2019 年 11 月对于新版本的vue提供的组合Api进行的尝试&#xff0c;它可以很好的集合vue新的api方法&#xff0c;且还很好的支持ts的写法&#xff0c;Pinia…

web前端-javascript-运算符的优先级(如果遇到的优先级不清楚的,可以使用()来改变优先级)

文章目录运算符的优先级1. , 运算符2. 优先级2.1. 就和数学中一样&#xff0c;在 JS 中运算符也有优先级2.2. 在 JS 中有一个运算符优先级的表2.3. 但是这个表我们并不需要记忆2.3. &&和||的优先级运算符的优先级 var a, b, c;//var a1, b2 , c3; //alert(b);//var re…

sql server如何卸载干净?来看这里

一、如何卸载干净 1.关闭服务 快捷键&#xff1a;windows R&#xff0c;在命令行输入&#xff1a; services.msc&#xff0c;把有关SQL都关闭 &#xff0c;下图所示&#xff1a; 2.到控制面板&#xff0c;卸载 sql server 3.删除磁盘里的文件 我的在c盘里&#xff0c;看各位…

你了解专利的快速预审嘛?

随着经济的发展和科技创新步伐的加快&#xff0c;我国专利申请量的增长速度已大大高于专利审结的速度。专利审查周期的长短不仅影响企业对市场的可预期性&#xff0c;而且影响专利系统对技术创新的产出和扩散的激励作用的发挥。过长的专利审查周期可能会影响企业的竞争预期和获…

[附源码]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…

为什么选择WordPress作为企业CMS?

WordPress 是世界上最受欢迎的内容管理系统 (CMS)。它为超过40% 的网站和超过 64% 的使用 CMS 的网站提供支持。它易于使用和定制。但它是企业网站的最佳选择吗&#xff1f; 随着大公司意识到它能够构建一个可以根据他们的需求扩展的强大网站的能力&#xff0c;WordPress持续流…

JVM知识体系学习一:JVM基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用

文章目录前言一、JVM基础1、cross platform 跨平台2、cross language 跨语言3、什么是JVM呢&#xff1f;一张图告诉你4、java从编码到执行*****5. 从跨平台的语言到跨语言的平台6. jvm与class文件格式7. JVM8. javac的过程9. 常见的JVM实现10. JDK JRE JVM二、Class File Forma…

Java多线程(二)——Thread类的相关方法

Thread类的构造方法 Thread() class MyThread extends Thread {Overridepublic void run() {System.out.println("hello Thread");} } public class ThreadDemo {public static void main(String[] args) {Thread t new MyThread();t.start();System.out.println(&…

java数据结构与算法 --- 第十章 数结构基础

第十章 树结构基础 I 引和基本概念 为什么需要树结构? 数组,查询快,增删慢 链表… 而树结构,同时提高查询和增删! 基本概念 术语: 有手就行 II 二叉树 1.概念: 二叉树:每个节点最多有两个子节点的数叫二叉树 满二叉树: 所有叶子节点都在最后一,结点的总数是2^n-1(n是层数…

jeecg-boot中上传图片到华为云obs云存储中

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 前言 jeecg-boot框架中&#xff0c;其实对接的功能还是挺多的&#xff0c;其中就有文件云存储服务器&#xff0c;不过是阿里云oss的&#xff0c;那如果我们使用的是七牛云&#xff0c;或…