C++学习------智能指针------(智能pointer)

智能指针的由来和概念

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指针中的引用计数器的数量增加,并且它的析构也不会导致引用计数器的减少。(例如在链表的指向问题中。)

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值