C++智能指针详解

注:源码剖析在代码注释展现

了解智能指针

  • 头文件 #including< memory >

  • 版本

      VC版本自动指针auto_ptr
      VS版本自动指针auto_ptr
      boost库的六种智能指针
      	局部指针:			scoped_ptr、scoped_array
      	共享计数器指针:		shard_ptr、scoped_array
      	弱指针:				weak_ptr
      	侵入式引用计数指针:	intrusive_ptr
      C++11参考boost实现的:
      	week_ptr、shared_ptr、unique_ptr(对应scoped_ptr)
    

分析智能指针

  • RAII
  • 智能
  • 指针

RAII思想
智能指针的产生,就是程序员希望实现 利用对象的生命周期来控制程序资源,
在构造时获取资源,这样使资源的访问在对象的生命周期内始终保持有效,
在析构时释放资源,这样使程序员不需要再显示的释放资源。
智能
智能就体现在,它使用构造方法和析构方法对空间进行自动获取和释放。这就是RAII思想的实践。

所以不论是最早的auto_ptr自动指针还是最重要的shared_ptr引用计数指针,智能指针的出发点都是资源的自动管理。
指针
说他是指针,是因为重载了 *取地址和->指向符。
使用

int* ptr = new int(10);
auto_ptr<int> pa(ptr);
cout<< *pa <<endl;

string *ptr = new string("abcxyz");
auto_ptr<string> pa(pstr);
cout<< pa->size() <<endl;

VC版本的自动指针模拟源码剖析

VC版本的自动指针,是最早的使用RAII思想的代码,在一段时间中风靡全球,虽然技术已经有所精进,但它仍不失为我们学习的一个好的模板样例。
它使用用_Owns成员控制空间拥有权,优点是使用RAII对资源进行自动管理,缺点是只是对拥有权进行标识,但实际没有拥有权的对象也可以对空间进行操作。

template<class _Ty>
class auto_ptr
{
public:
	auto_ptr(_T* p = 0):_Owns(p != 0),_Ptr(p){}
	~auto_ptr()
	{
		//由于这样的析构方法和浅拷贝,在拷贝构造和赋值的时候就需要尤其注意拥有权的转移,
		//不然很容易造成一块空间被释放两次,或空间得不到释放(内存泄漏)
		if(_Owns) 
			delete _Ptr;
	}
	auto_ptr(const auto_ptr<_Ty>& Y):_Owns(Y._Owns),_Ptr(Y.release())
	{//通过release完成对Y拥有权的释放,同时将Ptr返回}
	_Ty* release()const
	{
		//在构造函数传参是,我们通常不希望对其做出改变,传入常引用,
		//在常的前提下如何保证可以对拥有权的改变呢?VC的做法如下:
		((auto_ptr<_Ty>*)this)->_Owns = false;
		//我们也可以通过对拥有权成员_Owns附加mutable关键字突破const的限制。达到对拥有权改变
		return _Ptr;
	}
	auto_ptr& operator=(const auto_ptr<_Ty>& Y)
	{
		if(this != &Y)
		{
			//为保证同一块空间不被释放多次和内存泄漏
			//需要先判断是不是同一块空间,对应不同的处理方法
			if(_Ptr != Y._Ptr)
			{	
				if(_Owns)
					delete _Ptr;
				_Owns = Y._Owns;
			}
			//如果this和Y不是同一块空间,那么即代表this只需要拥有权转移即可
			else if(Y.Owns)
			{
				_Owns = true;
			}
			//修改_Ptr的指向并释放Y拥有权
			_Ptr = Y.release();
		}
		return *this;
	}
	_Ty* operator->()const
	{return _Ptr;}       //->指向符完成调用成员函数或成员,需要返回地址
	_Ty& operator*()const
	{return *_Ptr;}		//*完成解引用或对值得修改,需要返回值得引用
private:
	bool _Owns;      //mutable bool _Owns;
	_Ty _Ptr;
};

VS版本的自动指针模拟源码剖析

很明显,VC版本的缺点就是拥有权的转移仅仅只是表面现象,实际上转移后的对象还是可以对空间进行操作的。这时不合理的。
VS版本得自动指针将VC版本得拥有权转化为对成员指针的赋空,失去管理权的对象不能再对原来的空间进行操作了。
这一点,与C++11右值引用应用中的移动构造还是相同的。

template<class _Ty>
class auto_ptr
{
	private:
		_Ty* _Ptr;
	public:
		auto_ptr(_Ty* p = 0):_Ptr(p){}
		~auto_ptr(){delete _Ptr;}
		//构造函数通过release函数完成对Y._Ptr的返回和Y拥有权的释放(ptr赋空)
		~auto_ptr(auto_ptr<_Ty>& Y):_Ptr(Y.release()
		{//这里显然是不能使用const传参了,为什么,难道设计师就不知道嘛?}
		_Ty* release()
		{
			//使用临时变量的思想也不失为RAII,这在scoped_ptr中有更加突出的表现
			_Ty* tmp = Y._Ptr;
			Y._Ptr = nullptr;
			return tmp;
		}
		auto_ptr& operator=(auto_ptr<_Ty> Y)
		{
			if(this != &Y)
			{				
				//release完成Y对象的空间释放和转移
				//使用reset完成了this空间的释放和拥有权转移
				reset(Y.release());
			}
			return *this;
		}
		void reset(_Ty* p)
		{
			//加上条件,防止同一块空间相互赋值
			if(_Ptr != p)
				_Ptr = nullptr;
			_Ptr = p;
		}
		T*
		
}

auto_ptr当前的实现只能对单一对象进行管理,没有能力对数组空间进行管理。

如:int* ptr = new int[10];
	就不能使用auto_ptr接收ptr构造对象

其原因就是:

  • RAII析构函数在析构时,智能delete _Ptr释放单个空间
  • 没有重载new[ ]

boost库的智能指针可以让解决这种问题。

boost库的六种智能指针

scoped_ptr:

scoped 英文释义:局部、范围

它从命名上就展示了它的性质: 拥有权不能发生转移。
操作上不难想到: 它将 赋值语句、拷贝构造、operator!=()、operator==()这几个拥有权可能会发生改变的函数全部私有化了。
所以用法上,我们也需要记住,是不能拷贝构造和赋值的。
而且scoped_ptr重载了auto_ptr传参的构造方法。

int *ptr = new int(10);
scoped_ptr<int> s1(ptr);
scoped_ptr<int> s2(ptr);
scoped_ptr<int> s3(s1);   错误!
s2 = s3;				  错误!   
auto_ptr<int> s4(ptr);
scoped_ptr<int> s5(s4);   正确!

值得一说的是,scoped_ptr的优点之一在于它的reset( )函数上,
scoped_ptr不能发生拥有权的转移,可是它通过reset()方法实现了空间的赋值。
它的源码是这样的:

typedef scoped_ptr this_type;
void reset(T* p = 0)
{
//通过创建一个临时对象指向p空间,然后再与this空间进行交换
//交换完成之后,临时对象被释放,this空间得到改变,p对象指向的空间也没有发生改变
//通过这种方法,突破了拥有权,实现了空间的重置
//而这种借助临时对象自动释放的原理,也不失为一种RAII思想,体现了它的智能。
	this_type(p).swap(*this);
}
void swap(scoped_ptr &b)
{
	T* tmp = p.px;
	b.px = px;
	px = tmp;
}
int main()
{
	int *ptr = new int(10);
	scoped_ptr<int> p(ptr);
	int* qtr = new int(20);
	p.reset(qtr);
	return 0;
}

scoped_array:

命名上就告诉我们,它与scoped_ptr大致相同,也是不能进行拥有权的转移。
将拷贝构造、赋值语句等都进行了私有化。
但与之不同的是:
	重载了new[](不是单纯的new),在堆上分配动态数组,为动态数组提供代理,保证可以正确释放内存。
但是!!!
	它算是智能数组吧!没有重载*解引用和->指向符。
	也没有begin、end等迭代器的操作。

shared_ptr

重中之重,来了!
有一句话“ shard_ptr 在现在甚至未来,都是最先进的”。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值