【C++】浅析智能指针

C++中为什么要引入智能指针

  • malloc 或者new出来的空间没有进行释放,存在内存泄漏等问题
  • 异常安全问题,如果在malloc和free之间存在异常抛出,程序转移到异常处执行还是会存在内存泄漏问题

智能指针的使用及原理

1.RAII
RAII只一种利用对象生命周期来控制程序资源(如内存,操作句柄,网络连接,互斥量等),在对象构造时获取资源,接着控制对资源的访问在对象的生命周期内始终有效,最后在对象析构的时候释放资源,实际上就是把管理一份资源的责任托付给了一个对象

  • 不需要显示的释放资源
  • 保证对象所需的资源在其生命周期内始终有效
#include <iostream>
using namespace std;

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

	~SmartPtr()
	{
		if (_ptr)
		{
			delete _ptr;
		}
	}
private:
	T* _ptr;
};

void MergeSort(int* a, int n)
{
	int* tmp = (int*) malloc(sizeof(int*));
	SmartPtr<int> sp(tmp);

	//处理其他的逻辑功能
}

int main()
{
	try
	{
		int a[5] = { 2,1,7,5,9 };
		MergeSort(a, 6);
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	system("pause");
	return 0;
}

上述这种SmartPtr还不能够称为智能指针,因为指针还要有解引用*和->的功能。我们在AutoPtr中对*和->功能进行重载。

#include <iostream>
using namespace std;

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;
	}

private:
	T* _ptr;
};

void MergeSort(int* a, int n)
{
	int* tmp = (int*) malloc(sizeof(int*));
	SmartPtr<int> sp(tmp);

	//处理其他的逻辑功能
}

struct Date
{
	int _year;
	int _month;
	int _day;
};

int main()
{
	SmartPtr<int> sp1(new int);
	*sp1 = 10;
	cout << *sp1 << endl;
	SmartPtr<Date> sparray(new Date);
	sparray->_year = 2000;
	sparray->_month = 1;
	sparray->_day = 1;
	system("pause");
	return 0;
}

智能指针的原理:
1.RAII特性
2.重载operator*和operator->,具有指针一样的行为

2.3 std::auto_ptr

c++98中就提出了auto_ptr智能指针

#include <iostream>
#include <memory>

using namespace std;
class Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}

	int _year;
	int _month;
	int _day;
};

int main()
{
	auto_ptr<Date> ap(new Date);
	auto_ptr<Date> ap(new Date);

	//auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了
	//C++98中设计的auto_ptr问题是非常明显的,所以实际很多公司明确规定了不能使用auto_ptr
	ap->_year = 2018;
	return 0;
}

auto_ptr的实现原理

#include <iostream>
using namespace std;

template <class T>
class Auto_Ptr
{
public:
	Auto_Ptr(T* ptr = NULL)
		:_ptr(ptr)
	{}
	~Auto_Ptr()
	{
		if (_ptr)
			delete _ptr;
	}

	//一旦触发拷贝,就将ap中的资源转移到当前对象中,然后令ap与其所管理的资源断开连接
	//这样就解决了一块空间被多个对象使用造成程序崩溃的问题
	Auto_Ptr(Auto_Ptr<T>& ap):
		_ptr(ap._ptr)
	{
		ap.ptr = NULL;
	}

	Auto_Ptr<T>& operator=(const Auto_Ptr<T>& ap)
	{
		//检测是否给自己赋值
		if (this != &ap)
		{
			if(_ptr)
			{
				delete _ptr;
			}

			_ptr = ap._ptr;
			ap._ptr = NULL;
		}
		return *this;
	}

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

private:
	T * _ptr;
};

C++11提供了更靠谱的std::unique_ptr
unique_ptr简单粗暴的防止拷贝来解决多个对象共享同一份资源的问题

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

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

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

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

private:
	//C++98防止拷贝的方式,只声明不定义+声明成私有
	UniquePtr(UniquePtr<T> const&);
	UniquePtr& operator=(UniquePtr<T> const&);

	//C++11防止拷贝的方式:delete
	UniquePtr(UniquePtr<T> const&) = delete;
	UniquePtr& operator=(UniquePtr<T> const&) = delete;
private:
	T * _ptr;
};

shared_ptr是C++11中提供的支持拷贝的智能指针

int main()
{
	//shared_ptr通过引用计数支持智能指针对象的拷贝
	shared_ptr<Date> sp(new Date);
	shared_ptr<Date> copy(sp);
 
	cout << "ref count:" << sp.use_count() << endl;
	cout << "ref count:" << copy.use_count() << endl;
	return 0;
}

shared_ptr的实现原理:通过引用计数的方式实现多个shared_ptr之间的资源共享,例如同一间教室的人共享一个点灯,但是只有最后离开的人需要关灯。

  • shared_ptr在其内部,给每个资源都维护了一份计数,用来记录资源被多少个对象共享;
  • 在对象被销毁时(析构函数调用时),说明自己不使用该资源了,引用计数减1;
  • 如果引用计数为0,说明自己是最后一个使用该资源的对象,必须释放该资源;
  • 如果不是0,说明除了自己还有其他对象在使用该资源,不能释放该资源,否则其他对象会变成野指针;

//模拟实现简单的shared_ptr

template <class T>
class Shared_Ptr
{
public:
	Shared_Ptr(T* ptr = nullptr)
		:_ptr(ptr),
		_pRefCount(new int(1)),
		_pMutex(new mutex)
	{}

	~Shared_Ptr()
	{
		Release();
	}

	Shared_Ptr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr),
		_pRefCount(sp._pRefCount),
		_pMutex(sp._pMutex)
	{
		AddRefCount();
	}

	shared_ptr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			//释放管理的旧资源
			Release();

			//共享管理新对象的资源,并增加引用计数
			_ptr = sp._ptr;
			_pRefCount = sp._pRefCount;
			_pMutex = sp._pMutex;

			AddRefCount();
		}
		return *this;
	}

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

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

	void AddRefCount()
	{
		//加锁或者使用加1的原子操作
		_pMutex->lock();
		++(*_pRefCount);
		_pMutex->unlock();
	}


private:
	void Release()
	{
		bool deleteflag = false;
		//引用计数减1,如果减到1,释放资源
		_pMutex->lock();
		if (--(*_pRefCount) == 0)
		{
			delete _ptr;
			delete _pRefCount;
			deleteflag = true;
		}
		_pMutex->unlock();

		if (deleteflag == true)
			delete _pMutex;
	}
private:
	int* _pRefCount;  //引用计数
	T* _ptr;   //指向管理资源的指针
	mutex* _pMutex;  //互斥锁
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值