智能指针

智能指针

今天我们主要就是来说一下智能指针,在前面的c语言阶段,我们就没有接触过这些东西,但是通过对c++知识的进一步了解,我们会发现c++存在内存泄露问题和在上一篇博客中提到的异常问题。

1.智能指针的引入

首先来看一下这段代码,分析一下他有没有什么内存问题。

#include <iostream>
#include <vector>
using namespace std;

void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right)
		return;
	int mid = left + ((right - left) >> 1);
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid+1, right, tmp);

	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
			tmp[index++] = a[begin1++];
		else
			tmp[index++] = a[begin2++];
	}
	while (begin1>end1)
		tmp[index++] = a[begin1++];
	while (begin2<end2)
		tmp[index++] = a[begin2++];
	memcpy(a + left, tmp + left, sizeof(int)*(right - left + 1));
}

void Mergesort(int *a, int n)
{
	int *tmp = (int*)malloc(sizeof(int)*n);
	_MergeSort(a, 0, n - 1, tmp);

}

int main()
{
	int a[5] = { 4, 5, 2, 3, 1 };
	Mergesort(a, 5);
	return 0;
}

问题分析:
1.malloc出来的空间没有进行释放,存在内存泄漏问题
2.异常安全问题,如果在malloc和free之间存在抛异常,那么还是内存泄漏问题。

2.智能指针的使用

通过上面的代码我们可以看出,智能指针在C++中的应用,是主要针对内存泄漏问题,
2.1 RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源 接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
1、不需要显示的释放资源
2、采用这种方式,对象所需的资源在其生命期内始终保持有效。
RAII的优缺点:
优势:用户不需要关心资源何时去释放
缺陷:浅拷贝----不能采用深拷贝的方式解决
2.2 智能指针的原理
智能指针原理:RAII + 类对象具有类似指针的行为(operator*()/operator->()) + 解决浅拷贝

template<class T>
class Smartptr
{
public:
	Smartptr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~Smartptr()
	{
		if (_ptr)
			delete _ptr;
	}
	//让该类的对象具有指针类似的行为
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
protected:
	T* _ptr;
};

struct A
{
	int a;
	int b;
	int c;
};

void TestFunc1()
{
	Smartptr<int> sp(new int);
	*sp = 100;
	//解引用
	Smartptr<A> sp1(new A);
	sp1->a = 10;
	sp1->b = 20;
	sp1->c = 30;
}

int main()
{
	TestFunc1();
	return 0;
}
3.智能指针的分类

3.1 auto_ptr
1.C++98版本为我们提供了auto_ptr的智能指针。
(1)原理:RAII + 类对象具有类似指针的行为(operator*()/operator->()) + 解决浅拷贝(资源的转移)
(2)存在的问题:当对象拷贝或复制后,前面的对象就悬空了,如果在访问前面的对象就会中断代码**。多个对象不能同时访问一份资源。**

#include <iostream>
using namespace std;

namespace bite
{
	template<class T>
	class auto_ptr
	{
	public:
		//RAII
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		auto_ptr(auto_ptr<T>& ap)
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;
		}

		auto_ptr<T>& operator=(auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				if (_ptr)
					delete _ptr;
				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
			return *this;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

		//让类具有指针类似的行为
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	protected:
		T* _ptr;
	};
}

void TestFunc()
{
	//常规指针
	int a = 10;
	int* p1 = &a;
	int* p2(p1);

	*p1 = 100;
	*p2 = 200;
	bite::auto_ptr<int> ap1(new int);
	
	//解决方式:将资源转移
	bite::auto_ptr<int> ap2(ap1);
	*ap1 = 10;//会发生崩溃----ap1已经将资源转移给ap2,自已与资源断开联系

}

int main()
{
	TestFunc();
	return 0;
}

2.改进:auto_ptr
原理:RAII + 具有指针类似行为 + 解决浅拷贝:资环管理权的转移(对资源释放的权利)
实现方式:在auto_ptr类中维护bool类型变量
_owner:ture代表该对象具有对资源释放的权利
缺陷:存在野指针问题

namespace bite
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			, _owner(false)
		{
			if (_ptr)
				_owner = true;
		}

		auto_ptr(const auto_ptr<T>& ap)
			:_ptr(ap._ptr)
			, _owner(ap._owner)
		{
			ap._owner = false;
		}

		auto_ptr<T>& operator=(const auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				//先处理以前旧资源
				if (_ptr && _owner)
					delete _ptr;
				_ptr = ap._ptr;
				_owner = ap._owner;
				ap._owner = false;
			}
			return *this;
		}
		~auto_ptr()
		{
			if (_ptr && _owner)
				delete _ptr;
		}

		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	protected:
		T* _ptr;
		mutable bool _owner;   //资源真正拥有者(对资源具有释放的权利)
	};
}

void TestFunc()
{
	bite::auto_ptr<int> ap1(new int);
	bite::auto_ptr<int> ap2(ap1);
	bite::auto_ptr<int> ap3;

	*ap1 = 10;
	*ap2 = 20;
	*ap3 = 30;
}

int main()
{
	TestFunc();
	return 0;
}

3.2 unique_ptr
unique_ptr智能指针原理:RAII + 具有指针类似行为 + 解决浅拷贝(资源独占的方式(一份资源只能被一个对象管理))
实现方式:禁止拷贝构造和赋值运算符重载调用—防拷贝
缺陷:多个unique_ptr的对象之间不能共享资源

#include <iostream>
using namespace std;

namespace bite
{
	template<class T>
	class unique_ptr
	{
		//RAII
	public:
		unique_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}
		~unique_ptr()
		{
			if (_ptr)
				delete _ptr;
		}

		//具有指针类似行为
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		
		//防止被拷贝--禁止调用拷贝构造&赋值运算符重载

#if 0
		//C++98:只声明不定义& private
	private:
		unique_ptr(const unique_ptr<T>& up);
		unique_ptr<T>& operator=(const unique_ptr<T>& up);
#endif
		//C++11中
		unique_ptr(const unique_ptr<T>& up) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;
	protected:
		T* _ptr;
	};
}

void TestFunc()
{
	bite::unique_ptr<int> up1(new int);
	bite::unique_ptr<int> up2;
}

int main()
{
	TestFunc();
	return 0;
}

3.3 shared_ptr
1.shared_ptr智能指针原理: RAII + 指针类似的行为 + 引用计数
2.缺陷:1. 不是线程安全 2. 只能管理new的资源 3.存在循环引用问题

#include <iostream>
#include <mutex>

using namespace std;

namespace bite
{
	template < class T >
	class shared_ptr
	{
	public:
		//RAII
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			, _pCount(nullptr)
			, _pMutex(nullptr)
		{
			if (_ptr)
			{
				_pMutex = new mutex;
				_pCount = new int(1);
			}
		}
		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			, _pCount(sp._pCount)
			, _pMutex(sp._pMutex)
		{
			if (_pCount)
				AddRef();
		}
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (this != &sp)
			{
				//让当前对象与资源断开
				Release();

				//与sp共享
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				if (_pCount)
					AddRef();
			}
			return *this;
		}
		~shared_ptr()
		{
			Release();
		}
		//具有指针类似行为
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		int use_count()
		{
			return *_pCount;
		}
	private:
		void AddRef()
		{
			_pMutex->lock();
			++(*_pCount);
			_pMutex->unlock();
		}
		int SubRef()
		{
			_pMutex->lock();
			--(*_pCount);
			_pMutex->unlock();

			return *_pCount;
		}
		void Release()
		{
			if (_ptr && 0 == SubRef())
			{
				delete _ptr;
				delete _pCount;

			}
		}
	protected:
		T* _ptr;
		int* _pCount;
		mutex* _pMutex; //保证计数的安全性,互斥锁
	};
}
//mutex:可以保证计数的安全性 || 可以保证shared_ptr类本身的安全性
//但不能保证shared_ptr管理资源中内容的安全性

void TestFunc()
{
	bite::shared_ptr<int> sp1(new int);
	bite::shared_ptr<int> sp2(sp1);
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;

	bite::shared_ptr<int> sp3(new int);
	bite::shared_ptr<int> sp4(sp3);
	cout << sp3.use_count() << endl;
	cout << sp4.use_count() << endl;

	sp3 = sp1;
	cout << sp1.use_count() << endl;

	sp4 = sp1;
	cout << sp1.use_count() << endl;
}

int main()
{
	TestFunc();
	return 0;
}

3.shared_ptr的线程安全问题
(1)智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2,这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以智能指针中引用计数++ 、–是需要加锁的,也就是说引用计数的操作是线程安全的。
(2)智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值