IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++学习------智能指针------(智能pointer) -> 正文阅读

[C++知识库]C++学习------智能指针------(智能pointer)

作者:recommend-item-box type_blog clearfix

智能指针的由来和概念

1.由来:我们可以先看下列代码:

int main()
{
 int* ptr = new int(10);
 return 0;
 }

我的上述代码有个明显的问题,那就是new出来的空间没有用delete进行释放,这样造成的结果就是有内存泄漏,由于在写程序中,我这个代码比较短,我们可以直接发现没有释放空间,那如果我们写到很多代码,又很杂,我们有时候会发现不了这些问题,所以我们就推出了一个指针叫做智能指针。
2.智能指针的概念:
可以自动的去管理内存的释放:也就是说,我们申请的空间在作用域结束的时候,我们不需要去调用释放内存的函数就可以将内存释放,防止了内存泄漏。

内存泄漏

上述我们一直在说内存泄漏,那么什么是内存泄漏呢?
1.内存泄漏:
内存泄漏是指因为疏忽,未能将程序开辟的空间在最后不使用的时候得到释放。而内存泄漏并不是物理内存的某段位置丢失,而是因为程序先前在物理内存上分配了空间,但是由于操作的失误,在没有释放这段内存的数据之前失去了对这段内存的控制,因而造成了内存泄漏。
2.内存泄漏的危害:
长期运行的程序出现内存泄漏后危害很大,影响也很大。如操作系统:出现内存泄漏后会慢慢积累,最终就会卡死了。
3.内存泄漏的分类:
①:堆内存泄漏:
堆内存是指,程序在运行中通过malloc/calloc/realloc/new从堆上申请的内存,用完后必须调用相应的free/delete进行释放。假如申请的内存没有得到释放,那么就会出现堆内存泄漏,并且以后这段内存都将无法使用。
②:系统资源泄漏:
指程序使用系统分配的资源,没有对应的函数进行释放,导致系统资源浪费,严重可导致系统效能减少,系统执行不稳定。
4.如何避免内存泄漏:
①:在内存开辟之后,释放内存。
②:采用RAII思想或智能指针来处理。
③:也可也用相应的内存检测工具去检测,然后改正。
总结为以下两点:
①:事前预防。
②:事后查错。

智能指针的分类及原理

首先我们在学习智能指针之前我们必须搞懂RAII思想是什么。

RAII思想

1.概念:是一种用对象声明周期来控制控制程序资源的一种简单技术。
由于在类中,示例化对象的时候会自动调用类中的构造函数,而对象的作用域结束的时候,又会自动调用类中的析构函数,由于这种操作,让我们有了RAII思想,在对象构造是获取资源,在对象析构时释放资源,这样就很可能的预防了内存泄漏。并且这样作有两大好处:

  • 不需要显式的释放资源
  • 采用这种方式,对象所需要的资源在其生命周期内始终保持有效。

下面用一段代码演示一下:

class RAII
{
public:
	RAII(int *P = nullptr) :a(P)
	{}
	~RAII()
	{
		if (a)
		{
			delete a;
		}
	}
private:
	int* a;
};

(上面这段代码假设是用int的类型进行)

智能指针的分类及原理

1.上述我们实现的例子中,还不能说明其就是一个指针,因为没有重载*,->等一些指针的操作。
进行一些添加,就可以让上述代码具有类似指针的作用:

class RAII
{
public:
	RAII(int *P = nullptr) :a(P)
	{}
	~RAII()
	{
		if (a)
		{
			delete a;
		}
	}
public:
	int& operator*()
	{
		return *a;
	}
	int* operator->()
	{
		return a;
	}
private:
	int* a;
};

这样就具有了指针的一些作用,所以智能指针的主要性质为:

  • RAII思想
  • 重载了一些如*,->等指针具有的操作

接下来我们说一些库里面的智能指针:
1.auto_ptr智能指针:
实现原理:管理权转移。
意思是,这个智能指针的使用,他不能让两个指针去指向同一个空间,也就是说,一个空间只能让一个智能指针去指向。
下面我们模拟实现一下:

template<class T>
class AutoPtr
{
public:
	AutoPtr(T *P = nullptr) :a(P)
	{}
	AutoPtr(AutoPtr<T>& P) :a(P.a)
	{
		P.a = nullptr;//取消原有指针的控制
	}
	AutoPtr& operator = (AutoPtr<T>& P)
	{
		if (&P != this)//检测是不是给自己赋值
		{
			if (a)//如果自己曾经有指向的位置,那么将其位置释放后,再重新赋值
			{
				delete a;
			}
			a = P.a;
			P.a = nullptr;
		}
		return *this;
	}
	~AutoPtr()
	{
		if (a)
		{
			delete a;
		}
	}
public:
	T& operator*()
	{
		return *a;
	}
	T* operator->()
	{
		return a;
	}
private:
	T* a;
};

但是auto_ptr有个缺点就是不能对数组空间进行操作,所以推出了一个auto_array,主要是对数组空间进行操作,其原理和实现和auto_ptr相似。
2.unique_ptr智能指针和scoped_ptr智能指针:
①:unique_ptr智能指针:
在是对atuo_ptr进行了修改,主要的修改是禁止拷贝构造:
代码如下:

template<class T>
class UniquePtr
{
public:
	UniquePtr(T *P = nullptr) :a(P)
	{}
	UniquePtr(UniquePtr<T>& P) :a(P.a)
	{
		P.a = nullptr;//取消原有指针的控制
	}
	~UniquePtr()
	{
		if (a)
		{
			delete a;
		}
	}
public:
	T& operator*()
	{
		return *a;
	}
	T* operator->()
	{
		return a;
	}
private:
	UniquePtr& operator = (AutoPtr<T>& P);
private:
	T* a;
};

将拷贝构造函数设置为私有成员,这样就直接暴力防止了拷贝的发生。
②:scope_ptr智能指针:
主要也是对auto_ptr智能指针进行了修改,而在unique_ptr的基础上不能进行赋值语句。
代码如下:

template<class T>
class UniquePtr
{
public:
	UniquePtr(T *P = nullptr) :a(P)
	{}
	~UniquePtr()
	{
		if (a)
		{
			delete a;
		}
	}
public:
	T& operator*()
	{
		return *a;
	}
	T* operator->()
	{
		return a;
	}
private:
	UniquePtr& operator = (AutoPtr<T>& P);
	UniquePtr(UniquePtr<T>& P);
private:
	T* a;
};

直接将赋值语句和拷贝构造函数都移动到私有成员下面,直接进行暴力禁止。
对于这两个指针:
①:unique_ptr拥有一件物品。不可复制但支持所有权转让,它是作为现在不推荐的替代品而引入的。
②:scoped_ptr是既不可复制也不可移动。当想要确保在超出作用域时删除指针时,这是首选的选择。

3.share_ptr智能指针
share_ptr智能指针,是目前最受欢迎的指针,由于它的实现提供了多个指针指向同一个空间,使这种它的出现可以说是打败了所有目前存储的指针,无敌的存在,下面我们来学习一下share_ptr智能指针:
①:首先,它支持拷贝构造和赋值语句。
②:原理:是通过引用计数器的方式来实现多个share_ptr对同一个指针进行管理:

  • share_ptr内部,给每个资源都有一份计数器,用来记录有几个share_ptr指针共同维护的同一个资源.
  • 当有对象被销毁的时候,那么这个计数器就自动减一。
  • 如果引用计数器为0,那么便调用相应的析构函数,将这一资源释放掉。
  • 如果引用计数器不为0,那就不能释放这一资源,因为还有其他share_ptr智能指针指向这个资源。

简单模拟实现:

template<class T>
class SharePtr
{
public:
	SharePtr(T *P = nullptr) : a(P)
		, _pRefCount(new int(1))
	{}
	SharePtr(SharePtr<T>& P) :a(P.a)
	{
		*_pRefCount++;
	}
	SharePtr& operator = (SharePtr<T>& P)
	{
		if (&P != this)
		{
			if (a)
			{
				delete a;
			}
			a = P.a;
		}
		*_pRefCount++;
		return *this;
	}
	~SharePtr()
	{
		Release();
	}
public:
	T& operator*()
	{
		return *a;
	}
	T* operator->()
	{
		return a;
	}
public:
	void Release()
	{
		if (--(*_pRefCount) == 0)
		{
			delete a;
		}
	}
private:
	T* a;
	int* _pRefCount; // 用于引用计数
};

这里面的模拟实现只是对shart_ptr有一个简单的认识,主要是有了引用计数器的引入。这样更好的提供了接口,让智能指针和指针的操作基本相似了。
4.weak_ptr智能指针:
weak_ptr智能指针(指针如其名字):为弱指针,这个指针存在功能主要是为了配合share_ptr智能指针,并且不具有指针的操作如:*,->等一些操作。
作用:在与share_ptr使用时,用它声明的指针在指向时不会导致shart_ptr指针中的引用计数器的数量增加,并且它的析构也不会导致引用计数器的减少。(例如在链表的指向问题中。)

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 15:59:31  更:2022-04-06 16:02:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:18:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码