C++智能指针简单剖析

智能指针的意义

在C++中,new和delete申请和销毁堆内存的关键字,而程序员在进行堆内存申请之后,常常容易忘记delete释放堆内存,而这也就容易造成一种现象–内存泄漏,也就是一块堆内存被申请之后,没有被释放,导致其他程序也不能使用这块堆内存,从而造成堆内存的浪费,降低了c++的开发效率。在java等语言中,只有new的关键字,而没有delete关键字,这就是因为java语言内存回收机制,而由于之前C++对于代码运行效率的极度追求,所以也没有引入这种机制。但是。随着C11标准等的发布,智能指针逐渐被大家所接受,虽然会降低一点效率,也大大的减少了开发人员在开发过程遇到的内存泄漏等问题。

智能指针并不是一个指针,而是一类模板,通过智能指针实例化出来的对象可以在对象销毁的时候,自动释放内存,而不用开发人员手动释放。

智能指针的实现思路

对于一个对象的生成和销毁,分别有以下的步骤:
对象的生成:

  • 开辟堆内存;
  • 调用构造函数。

对象的销毁:

  • 调用析构函数;
  • 释放内存;

根据对象销毁时的重要特性:自动调用析构函数 ,我们只需要将delete释放内存的函数写到析构函数中,则完成了在对象销毁时,系统可以自动的释放堆内存,从而避免了内存泄漏等问题。

C++11标准下的智能指针

C++为开发者提供了两类智能指针:

  • 带引用计数的智能指针
  • 不带引用计数的智能指针

分别是auto_ptr,unique_ptr,shared_ptr,weak_ptr等智能指针。其中auto_ptr是在c98标准中就引入到的智能指针,而剩余的几种智能指针则是后来参考boost库中智能指针的实现而引入到C++中。他们都存在与memory库中,所以在使用智能指针时

auto_ptr

auto_ptr早在c98标准发布是就存在于STL库中,但现在由于各种各样的性能问题而被弃用。先研究一下auto_ptr的源代码:

template<class _Ty>
	class auto_ptr
		{	// wrap an object pointer to ensure destruction
public:
	typedef auto_ptr<_Ty> _Myt;
	typedef _Ty element_type;

	// 构造函数
	explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()
		: _Myptr(_Ptr)
		{	// construct from object pointer
		}
	
	// 拷贝构造函数,调用release方法
	auto_ptr(_Myt& _Right) _THROW0()
		: _Myptr(_Right.release())
		{	// construct by assuming pointer from _Right auto_ptr
		}
	
	// 赋值运算符函数,调用release方法
	template<class _Other>
		_Myt& operator=(auto_ptr<_Other>& _Right) _THROW0()
		{	// assign compatible _Right (assume pointer)
		reset(_Right.release());
		return (*this);
		}
	// 析构函数
	~auto_ptr() _NOEXCEPT
		{	// destroy the object
		delete _Myptr;
		}
		
	// 指针解引用运算符重载
	_Ty& operator*() const _THROW0()
		{	// return designated value
 #if _ITERATOR_DEBUG_LEVEL == 2
		if (_Myptr == 0)
			_DEBUG_ERROR("auto_ptr not dereferencable");
 #endif /* _ITERATOR_DEBUG_LEVEL == 2 */

		return (*get());
		}
	
	// 指向符重载
	_Ty *operator->() const _THROW0()
		{	// return pointer to class object
 #if _ITERATOR_DEBUG_LEVEL == 2
		if (_Myptr == 0)
			_DEBUG_ERROR("auto_ptr not dereferencable");
 #endif /* _ITERATOR_DEBUG_LEVEL == 2 */

		return (get());
		}
	
	// get方法——获取指针地址
	_Ty *get() const _THROW0()
		{	// return wrapped pointer
		return (_Myptr);
		}
	
	// 剥夺原auto_ptr对指针的拥有权,赋予当前auto_ptr对指针的拥有权
	_Ty *release() _THROW0()
		{	// return wrapped pointer and give up ownership
		_Ty *_Tmp = _Myptr;
		_Myptr = 0;
		return (_Tmp);
		}

	// 销毁指定对象并存储新指针
	void reset(_Ty *_Ptr = 0)
		{	// destroy designated object and store new pointer
		if (_Ptr != _Myptr)
			delete _Myptr;
		_Myptr = _Ptr;
		}

private:
	_Ty *_Myptr;	// the wrapped object pointer
	};

Shared_ptr

shared_ptr和别的智能指针最大的区别是加入了引用计数,即可以对一块资源的使用对象进行计数,如果这块资源的使用对象变为0,则需要删除这一块内存。但是通常情况下要搭配weak_ptr使用。
代码如下:

ount;
};
trmplate<class T>
class Shard_ptr{
	public:
		Shared_ptr(T* p=0):_ptr(p)
		{
			cnt=new Counter;
			if(p)
			{
				cnt->shardCount=1; 
			}
		}
		~Shared_ptr()
		{
			release();
		}
		Shared_ptr(Shared_ptr<T> const &src)  //拷贝构造 
		{
			_ptr=src._ptr;
			//原shared对象的 
			(src.cnt)->shardCount++;
		}
		Shared_ptr(WeakPtr<T>&const w)
		{
			//复制weakptr对象指针 
			_ptr=w._ptr;
			(w.cnt)->sharedCount++;
			cnt=w.cnt;
			
		}
		Shared_ptr<T>& operator=(Shared_ptr<T>& src)
		{
			//防止自我引用 
			if(this!=&src)
			{
				//释放源管理对象的所有权 
				release(); 
				(src.cnt)->sharedCount++;
				cnt=src.cnt;
				_ptr=src._ptr; 
			}
			return *this;
		 } 
		T& operator*()
		{
			return* _ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		friend class WeakPtr<T>;
	protected:
		//释放源对象所有权,count为0,则回收内存 
		void release()
		{
			cnt->shardCount--;
			
			if(cnt->shardCount<1)
			{
				delete _ptr;
				if(cnt->weakCount<1)
				{
					delete cnt;
					cnt=NULL;
				}
			}
		}
	private:
		T* _ptr;
		Counter* cnt;
};

weak_ptr

weak_ptr的诞生就是为了配合Shared_ptr来使用的,它的拷贝构造函数是通过shared_ptr和weak_ptr对象来构造,没有重载*和->,并且使用lock()来获取一个可用的对象。weak_ptr()不拥有内存的使用权,所以不能直接使用资源。通常是使用lock()来生成一个Shared_ptr来配合使用。

template<class T>
class WeakPtr
{
	public:
		WeakPtr()
		{
			_ptr=0;
			cnt=new Counter();
		}
		WeakPtr(Sharedptr<T>& src):_ptr(src._ptr),_cnt(src.cnt)
		{
			cnt.weakCount++;
		} 
		WeakPtr(WeakPtr<T>& src):_ptr(src._ptr),cnt(src.cnt)
		{
			cnt.weakCount++;
		 } 
		 ~WeakPtr()
		 {
		 	release(); 
		 }
		 WeakPtr<T>& operator=(WeakPtr<T>& src)
		 {
		 	if(this!=*src)
		 	{
		 		//释放原来的管理对象 
		 		release();
		 		cnt=src.cnt;
		 		cnt->weakCount++;
		 		_ptr=src._ptr;
			 }
			 return *this;
		 }
		 WeakPtr<T>& operator=(WeakPtr<T>& src)
		 {
		 	if(this!=*src)
		 	{
		 		release();
		 		cnt=src.cnt;
		 		cnt->shardCount++;
		 		_ptr=src._ptr; 
			 }
			 return *this;
		 }
		 Sharedptr<T> lock()
		 {
		 	//将weakptr转成sharedptr 
		 	return Sharedptr<T>(*this);
		 }
		 bool expired()
		 {
		 	if(cnt)
		 	{
		 		if(cnt->shardCount>0)
		 		{
		 			return false;
				 }
			 }
			 return true;
		  } 
		  friend class Sharedptr<T>;
	protected:
		void release()
		{
			if(cnt)
			{
				cnt->weakCount--;
				if(cnt->weakCount<1 && cnt->s<1)
				{
					cnt=NULL;
				}
			}
		}
		
	private:
		T* _ptr;
		Counter* cnt;	
};

引入weak_ptr就是为了解决强智能指针Shared_ptr可能会导致交叉引用而导致计数器永远不会减到0而导致资源不能被释放。所以在定义对象是使用强智能指针,使用对象使用弱智能指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值