【C++】智能指针

news2024/11/24 16:19:22

一、资源的管理

RAII:Resource Acquisition Is Initialization的简称,其翻译过来就是“资源获取即初始化”,即在构造函数中申请分配资源,在析构函数中释放资源,它是C++语言中的一种管理资源、避免泄漏的良好方法。

C++语言的机制保证了,当创建一个类对象时,会自动调用构造函数,当对象超出作用域时会自动调用析构函数。RAII正是利用这种机制,利用类来管理资源,将资源与类对象的生命周期绑定。

利用对象生命周期来控制程序资源的简单技术
(对象生命周期--------------程序资源)

对象构造获取资源
接着控制对资源的访问使之在对象的生命周期内始终保持有效
最后在对象析构的时候释放资源

—>把管理一份资源的责任托管给了一个对象

二、动态内存的管理

new:在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化
delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存

问题:有时使用完对象后,忘记释放内存,造成内存泄漏的问题。

所谓的智能指针本质就是一个类模板,它可以创建任意的类型的指针对象,当智能指针对象使用完后,对象就会自动调用析构函数去释放该指针所指向的空间。

下面是智能指针的基本框架,所有的智能指针类模板中都需要包含一个指针对象构造函数析构函数

template <class T>
class smartptr
{
	public:
		smartptr(T* _ptr)//构造函数
			:ptr(_ptr)
			{}
		~smartptr()//析构函数
		{
			if(ptr != nullptr)
			{
				cout<<"smartptr:delete"<<endl;
				delete ptr;
				ptr = nullptr;
			}
		}
	private:
		T* ptr;//指针对象
};

代码解读

smartptr(T* _ptr) :ptr(_ptr) {}

使用初始化列表来初始化字段
smartptr(T* _ptr){
	ptr = _ptr;
}

三、智能指针的定义和使用

智能指针不是一个指针,它其实是一个对象。它是通过C++的RAII机制实现的。主要是利用C++中对象在释放的时候,会自动调用析构函数这一特性。

所以,当智能指针对象释放的时候,在智能指针对象的析构函数中来释放其管理的内存资源。这样,开发人员就不需要手动去释放已经分配的内存空间。

3.1 shared_ptr原理介绍

shared_ptr是智能指针的一种,不仅通过RAII机制来管理内存资源,还引入了引用计数来解决当多个智能指针指向同一块内存空间的时候,何时释放这块内存空间的问题。

也就是说,同一时刻可以有多个shared_ptr拥有一块内存空间的所有权,当最后一个shared_ptr被销毁时,这块内存空间的引用计数为0时,这块内存空间将被释放。


shared_ptr对象有两个指针,一个是指向管理的内存空间,一个是指向内存控制块,内存控制块中包含引用计数和其他的一些信息(删除器和分配器)。

shared_ptr<Object> t1(new Object());
shared_ptr<Object> t2 = t1;

用图表示如下:
在这里插入图片描述
t1释放的时候,引用计数减一,然后释放t1的内存空间,如下:
在这里插入图片描述
当t2释放的时候,引用计数会再减一,这时引用计数就会变成0,这时就会释放Object的内存空间和内存控制块的空间,同时t2对象的空间也会被释放。

3.2 实现代码

#include <iostream>
using namespace std;

template <class T>
class shared_ptr
{
public:
	shared_ptr(T* _ptr)
		:ptr(_ptr)
	{
		ptrcount = new int(1);
		mt = new mutex;
	}

	void AddCount()
	{
		mt->lock();
		(*prtcount)++;
		mt->unlock();
	}

	shared_ptr(shared_ptr<T>& sp)
		:ptr(sp.ptr)
		,ptrcount(sp.ptrcount)
		,mt(sp.mt)
	{
		AddCount();
	}

	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		if (sp.ptr != ptr)
		{
			Realse();
			ptr = sp.ptr;
			ptrcount = sp.ptrcount;
			mt = sp.mt;
			AddCount();
		}
		return *this;
	}

	~shared_ptr()
	{
		Realse();
	}

	int& use_count()
	{
		return *ptrcount;
	}

	void Realse()
	{
		bool deleteflag = false;
		mt->lock();
		if (--(*ptrcount) == 0)
		{
			delete ptrcount;
			delete ptr;
			ptrcount = nullptr;
			ptr = nullptr;
			deleteflag = nullptr;
		}
		mt->unlock();

		if (deleteflag == true)
		{
			delete mt;
			mt = nullptr;
		}
	}

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

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

private:
	T* ptr;
	int* ptrcount;
	mutex* mt;
};

3.3 shared_ptr使用方法

1)初始化

//通过构造函数初始化
shared_ptr<Test> t(new Test());
 
//使用make_shared来初始化智能指针
shared_ptr<Test> t = make_shared<Test>();

2)支持拷贝构造、赋值

shared_ptr<Test> t(new Test());
shared_ptr<Test> t1(t);
t1 = t;
  1. 获取原始指针
shared_ptr<Test> t = make_shared<Test>();
Test* pTest = t.get();

例子:

#include <iostream>
#include  <memory> // 需要包含这个头文件
using namespace std;

int main()
{
	// 使用 make_shared 创建空对象
	shared_ptr<int> p1 = make_shared<int>();
	*p1 = 78;
	cout << "p1 = " << *p1 << endl; // 输出78
	cout << p1 << endl;

	// 打印引用个数:1
	cout << "p1 Reference count = " << p1.use_count() << endl;

	// 第2个第3个 shared_ptr 对象指向同一个指针
	shared_ptr<int> p2(p1);
	shared_ptr<int> p3(p1);

	// 每个指针的引用计数
	cout << "p1 Reference count = " << p1.use_count() << endl;
	cout << "p2 Reference count = " << p2.use_count() << endl;
	cout << "p3 Reference count = " << p3.use_count() << endl;

	p1 = nullptr;
	cout << "p2 Reference count = " << p2.use_count() << endl;
	cout << "p3 Reference count = " << p3.use_count() << endl;
	return 0;
}

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

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

相关文章

python快速实现简易超级玛丽小游戏

《超级玛丽》是一款超级马里奥全明星的同人作品&#xff0c;也是任天堂公司出品的著名横版游戏。 《超级马里奥》是一款经典的像素冒险过关游戏。最早在红白机上推出&#xff0c;有多款后续作品&#xff0c;迄今多个版本合共销量已突破4000万套。其中的主角马里奥、路易、碧琪…

[附源码]计算机毕业设计JAVAjsp闲置物品线上交易系统

[附源码]计算机毕业设计JAVAjsp闲置物品线上交易系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM …

tensor和numpy相互转换

tensor转成numpy b a.numpy()import torcha torch.arange(5) b a.numpy() print(a) print(type(a)) print(b) print(type(b))numpy转成tensor b torch.tensor(a)import torch import numpy as npa np.ones(5) b torch.tensor(a) print(a) print(type(a)) print(b) prin…

Spring Cloud框架(原生Hoxton版本与Spring Cloud Alibaba)基础入门篇 ---- 搭建环境

springcloud官方文档&#xff08;Hoxton SR5&#xff09;&#xff1a;https://cloud.spring.io/spring-cloud-static/Hoxton.SR5/reference/htmlsingle/ springcloud中文文档&#xff1a;https://www.springcloud.cc/ springcloud中国社区文档&#xff1a;http://docs.springcl…

【C++】栈~~(很详细哦)

在前几天&#xff0c;我们刚一起学过顺序表&#xff0c;链表&#xff08;无头单向不循环&#xff0c;有头双向循环&#xff09;&#xff0c;这两种都属于线性表因为是一系列存储的。而以后的哈希表则是散列表 今天我们看一下栈 目录 1.栈的介绍 2.实现 3.题目 1.栈的介绍 …

mindspore::dataset::GetAffineTransform的输出与cv2的输出不同

在使用C进行推理时用到了函数mindspore::dataset::GetAffineTransform&#xff0c;但是输入相同的数据后&#xff0c;与Python的cv2中的同名函数cv2.getAffineTransform所输出的结果不同。 C Ascend310端测试核心代码 #include <iostream> #include <vector>#…

synchronized 关键字背后的锁升级流程

文章目录前言一、基本特点二、加锁过程总结前言 博主个人社区&#xff1a;开发与算法学习社区 博主个人主页&#xff1a;Killing Vibe的博客 欢迎大家加入&#xff0c;一起交流学习~~ 一、基本特点 结合多线程锁的策略, 我们就可以总结出, Synchronized 具有以下特性(只考虑 J…

基于51单片机的智能台灯设计

一.硬件方案 本文介绍了一种基于PWM调光的智能台灯设计。把单片机技术和PWM调光技术结合起来实现台灯光强的调节。即在不改变PWM方波周期的前提下&#xff0c;利用单片机控制PWM的占空比&#xff0c;从而来改变电压的大小实现灯光亮度的调节。 当人体在台灯的范围内且环…

linux驱动设备节点失踪之迷雾围城

前言 参考文章&#xff1a;无法生成设备节点 最后证实&#xff1a;是bootargs配置错误导致的&#xff0c;不过中间发现也是可以通过mdev -s间接解决的&#xff0c;算是学习经验吧。 misc驱动框架是linux内核驱动中最简单实用的框架了。记录一下今天调试misc驱动的问题。misc驱动…

笔试强训48天——day19

文章目录一. 单选1.二分查找的时间复杂度&#xff08;&#xff09;2. 有一个单向链表中有一个A、B两个相邻元素&#xff0c;有一个指针p指向元素A&#xff0c;现将一个指针r指向的S元素要插入3. 双向链表中有两个指针域,llink和rlink分别指向前驱和后继,设p指向链表中的一个结点…

spark底层原理理解--高级进阶

概念概念理解和解释备注窄依赖窄依赖指1个父RDD分区数据只被1个子RDD的分区使用&#xff0c;即一对一或多对一的关系。 分为两种映射情况&#xff1a;一个父RDD的分区对应于一个子RDD的分区&#xff0c;或者多个父RDD的分区对应于一个子RDD的分区。 1个子RDD的分区对应于1个父R…

深入理解JS作用域链与执行上下文

变量提升&#xff1a; 变量提升&#xff08; hoisting &#xff09;。 我可恨的 var 关键字&#xff1a; 你读完下面内容就会明白标题的含义&#xff0c;先来一段超级简单的代码&#xff1a; <script type"text/javascript">var str Hello JavaScript hoi…

【K8S】初探Kubernetes

文章目录什么是容器编排什么是KubernetesK8s 和 Docker 之间的关系Kubernetes的整体架构Master 里的组件构成Work Node 里的组件构成总结K8s 组件工作流程结束语什么是容器编排 在《Docker 进阶指南&#xff08;下&#xff09;- 使用Docker Compose编排多个容器》文章当中&…

文件缓冲区

本期介绍&#x1f356; 主要介绍&#xff1a;什么是文件缓冲区&#xff0c;文件缓冲区存在的意义是什么&#xff0c;文件缓冲区的证明&#x1f440;。 一、什么是文件缓冲区 每一个正在使用的文件&#xff0c;操作系统都会为其在内存中开辟一块区域&#xff0c;称之为&#xff…

【数据结构】带头双向链表的简单实现

目录前言链表的实现List.hList.c**ListCreate()****LTInit()****ListPushBack()****ListPopBack()****ListPrint()****ListPushFront()****ListPopFront()****ListFind()****ListInsert()****ListErase()**ListErase()test.c前言 该篇博客主要讲解了带头双向链表的实现和一些细…

Cadence Allegro DXF结构图的导入详细教程

很多消费类板卡的结构都是异形的&#xff0c;由专业的CAD结构工程师对其进行精准的设计&#xff0c;PCB布线工程师可以根据结构工程师提供的2D图&#xff08;DWG或DXF格式&#xff09;进行精准的导入操作&#xff0c;在PCB中定义板型结构。 同时&#xff0c;对于一些工控板或者…

Ajax--跨域与JSONP--案例-淘宝搜索

要实现的UI效果 获取用户输入的搜索关键词 为了获取到用户每次按下键盘输入的内容&#xff0c;需要监听输入框的 keyup 事件&#xff0c;示例代码如下&#xff1a; // 监听文本框的 keyup 事件$(#ipt).on(keyup, function() {// 获取用户输入的内容var keywords $(this).val…

支撑向量机

1、支持向量机算法原理 支持向量机&#xff08;Support Vetor Machine&#xff0c;SVM&#xff09;由Vapnik等人于1995年首先提出&#xff0c;在解决小样本、非线性及高维模式识别中表现出许多特有的优势&#xff0c;并推广到人脸识别、行人检测和文本分类等其他机器学习问题中…

HTML期末作业:基于html+css+javascript+jquery实现古诗词网页 学生网页设计作品 web前端开发技术 web课程设计 网页规划与设计

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

初学C语言有什么建议?

什么&#xff1f;开玩笑&#xff0c;新手学C语言&#xff1f; 确实新手不学C语言学什么呢&#xff1f;为什么这么推荐新手学C语言呢具体看看下面的解释吧&#xff1f; C的重要性 我总结了网上很多人的说法如下&#xff1a; C语言是计算机界公认的有史以来最重要的语言。C语…