智能指针

     智能指针

  •   概念
    所谓智能指针就是智能/自动的管理指针所指动态资源的释放。 现阶段的智能指针是一种通用实现技术,是使用引用技术。 

  • 发展历史
  •    第一阶段(C++98):auto_ptr ----自动指针
         auto_ptr 的主要思想是 管理权转移, 但其缺陷很大,当通过拷贝构造函数,通过操作符=赋值后,原来的那个智能指针对象就失效了。只有新的智能 指针对象可以有效使用了。
  •    第二阶段(C++03):即在Boost中的智能指针
         scoped_ptr —— 守卫指针  scoped_ptr的主要思想是 防拷贝
         share_ptr —— 共享指针       share_ptr的主要思想是 引用计数复杂    缺陷:循环引用 
         weak_ptr —— 弱智者,不能单独存在,辅助解决shared_ptr的循环引用问题
  •    第三阶段(C++11):
         unique_ptr    还是运用防拷贝的思想 
         share_ptr   
         weak_ptr----弱指针,不能单独存在,辅助解决shared_ptr的循环引用问题

  实现
  • AutoPtr
template<class T>
class AutoPtr
{
public:

	AutoPtr(T* ptr)
		:_ptr(ptr)
	{}

	~AutoPtr()
	{
		cout<<"指针被释放"<<endl;
		if(_ptr)
		{
			delete _ptr;
		}
	}

	//运算符的重载(像指针一样)
	T& operator* ()
	{
		return *_ptr;//*(this->_ptr)
	}

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

	//拷贝构造
	AutoPtr(AutoPtr<T>& ap)//管理权转移
		:_ptr(ap._ptr)
	{
		ap._ptr = NULL;
	}

	//ap1=ap2
	AutoPtr<T>& operator=(AutoPtr<T> &ap) 
	{
		if(this != &ap)
		{
			if(_ptr)
				delete _ptr;
			_ptr = ap._ptr;
			ap._ptr = NULL;
		}
		return *this;
	}

private:
	T* _ptr;
};
    分析: 这里是因为 ap2 完全的夺取了 ap1 的管理权。然后导致访问 ap1 的时候程序就会崩溃。 如果在这里调用 ap2 = ap1 程序一样会崩溃,原因还是 ap1 被夺走管理权,所以这种编程 思想及其不符合C++思想,所以它的设计思想就是有一定的缺陷。因此,一般不推荐使用 auto_ptr 智能指针
  •     ScopedPtr
template<class T>
class ScopedPtr
{
public:
	ScopedPtr(T* ptr)
		:_ptr(ptr)
	{}

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

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

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

private:
	ScopedPtr(const ScopedPtr<T> &sp);//不能定义为公有的,防止在类外实现
	ScopedPtr<T>& operator=(const ScopedPtr<T> &sp);

private:
	T *_ptr;
};

注: ScopedPtr 将拷贝构造和赋值运算符的重载定义为protected, 并且只声明不实现,这样就实现了 防拷贝 的原理。
  • SharedPtr
template<class T>
class SharedPtr
{
	friend class WeakPtr<T>;
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		,_refcount(new int(1))
	{}

	~SharedPtr()//当引用技术为0时,释放空间
	{
		cout<<"~SharedPtr()"<<endl;
		if(--(*_refcount)==0)
		{
			printf("ox%p\n",_ptr);
			delete _ptr;
			delete _refcount;
		}
	}

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

	//sp2(sp1)
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)
		,_refcount(sp._refcount)
	{
		(*_refcount)++;
	}

	//sp1 = sp2
	SharedPtr<T> &operator = (const SharedPtr<T>& sp)
	{
		if(_ptr != sp._ptr) //防止自已给自已赋值
		{
			if(--(*_refcount)==0)
			{
				delete _ptr;
				delete _refcount;
			}
			_ptr = sp._ptr;
			_refcount = sp._refcount ;
			(*_refcount)++;
		}
		return *this;
	}

private:
	T* _ptr;
	int* _refcount;//引用次数

};

注: share_ptr 因为它采用引用计数的思想,所有它是功能较为完善的,但是 share_ptr 还是有缺陷的,例如在解决  循环引用 问题上。
eg: 双向链表
  (1) 无智能指针的双向链表
      struct ListNode
{ 
	int _data;
	ListNode* _next;
	ListNode* _prev;

	~ListNode()
	{
		cout<<"ListNode"<<endl;
	}
};


void TestRec()
{	
	ListNode* cur = new ListNode;
	ListNode* next = new ListNode;
	cur->_next = next; 
	next->_prev = cur;
	delete cur;
	delete next;	
}


(2)含有智能指针的双向链表

struct ListNode
{ 
	int _data;
	SharedPtr<ListNode> _next;
	SharedPtr<ListNode> _prev;

	ListNode()
		:_data(0)
		,_next(NULL)
		,_prev(NULL)
	{}

	~ListNode()
	{
		cout<<"ListNode"<<endl;
	}
};


void TestRec()
{	
	SharedPtr<ListNode> cur(new ListNode);
	SharedPtr<ListNode> next(new ListNode);

	cur->_next = next; //加上这两句导致循环引用
	next->_prev = cur;
}
分析: 由运行结果知,智能指针cur、next并没有释放,若加cur->_next = next; next->_prev = cur; 这两句运行结果如下:

由结果可知,由于上面两句导致循环引用。

画图分析如下:

_next、_prev的生命周期依赖于结点的释放,而cur、next结点又依赖于_next、_prev的释放,所以导致循环引用。


解决方法:
使用弱引用的智能指针打破这种循环引用。

  •   WeakPtr
  weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
template <class T>
class WeakPtr
{
public:
	WeakPtr()
		:_ptr(NULL)
	{}

	WeakPtr(const SharedPtr<T> & sp)
		:_ptr(sp._ptr)
	{}

	WeakPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		_ptr = sp._ptr;
		return *this;
	}

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

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

private:
	T* _ptr;
};

struct ListNode
{ 
	int _data;
	WeakPtr <ListNode> _next;
	WeakPtr <ListNode> _prev;

	ListNode()
		:_data(0)
		,_next(NULL)
		,_prev(NULL)
	{}

	~ListNode()
	{
		cout<<"~ListNode"<<endl;
	}
};


void TestRec()
{	
	SharedPtr<ListNode> cur(new ListNode);
	SharedPtr<ListNode> next(new ListNode);

	cur->_next = next; 
	next->_prev = cur;

}
由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。
注:虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案,如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。

 

 





  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值