说起指针相信大家一定不陌生,那么智能指针呢?

首先,我们来看看,为什么要有智能指针?

例如

void Test2 ()
{
     int* p1 = new int(2);
     bool isEnd = true;

     //...
     if (isEnd )
    {
          delete p1 ;
          return;
    }
     //...

     delete p1;
}

在我们开辟空间时,在不用时,应该将其释放掉,但是代码中经常会忘掉释放动态开辟的资源,在碰到前面的逻辑处理时,也常常是如履薄冰,小心谨慎,尽管如此还是会一不小心就会泄漏,防不胜防,令人头疼不已。那么,就引出了智能指针的概念了。

什么事智能指针呢?

所谓智能指针就是智能/自动化的管理指针所指向的动态资源的释放。

我们看看库中的智能指针

wKiom1c0kNmDaYRrAABJvo2d89o964.png

接下来,简单实现一下主要的智能指针

#include<iostream>
using namespace std;
//template<class T>
//class AutoPtr
//{
//public:
//	AutoPtr(T* ptr)
//		:_ptr(ptr)
//	{}
//
//	AutoPtr( AutoPtr<T>& sp)
//	{
//		 _ptr = sp._ptr ;
//		sp._ptr = NULL;
//	}
//
//	~AutoPtr()
//	{
//		if(_ptr)
//		{
//			delete _ptr;
//		    _ptr = NULL;
//		}
//		
//	}
//
//	AutoPtr<T>& operator=(AutoPtr<T>& sp)
//	{
//		if(this != &sp)
//		{
//		 _ptr = sp._ptr ;
//		sp._ptr = NULL;
//		}
//	}
//	
//	
//	T& operator*()
//	{
//		return *_ptr;
//	}
//
//	T* operator->()
//	{
//		return _ptr;
//	}
//
//	T* GetPtr()
//	{
//		return _ptr;
//	}
//
//private:
//	T* _ptr;
//};
//
//template<class T>
//class ScoprPtr   //把拷贝构造和赋值运算符的重载方成私有或者保护
//{
//public:
//	ScoprPtr(T* ptr)
//		:_ptr(ptr)
//	{}
//
//	~ScoprPtr()
//	{
//		if(_ptr)
//		{
//			delete _ptr;
//		    _ptr = NULL;
//		}
//		
//	}
//
//	T& operator*()
//	{
//		return *_ptr;
//	}
//
//	T* operator->()
//	{
//		return _ptr;
//	}
//
//	T* GetPtr()
//	{
//		return _ptr;
//	}
//protected:
//	ScoprPtr<T>& ScoprPtr=(ScoprPtr<T>& sp);
//	ScoprPtr(ScoprPtr<T>& sp);
//private:
//	T* _ptr;
//
//};
template<class T>
class SharePtr         //引用计数的方法实现的
{
public:
	SharePtr(T* ptr)
		:_ptr(ptr)
		,_pcount(new int(1))
	{}
	SharePtr(SharePtr<T>& sp)
	{
		_ptr = sp._ptr;
		_pcount = sp._pcount;
		++*(sp._pcount);
	}
	~SharePtr()
	{
		if(--(*_pcount) == 0)
		{
			delete _ptr;
			delete _pcount;
		    _ptr = NULL;
			_pcount = NULL;
		}
		
	}
	SharePtr<T>& operator=(SharePtr<T>& sp)
	{
		if(this != &sp)
		{
		 _ptr = sp._ptr;
		_pcount = sp._pcount;
		++*(sp._pcount);
		}
	}
	/*SharePtr<T>& operator=(SharePtr<T> sp)
	{
		SharePtr<T> tem(sp);
		swap(_ptr,tem._ptr);
		swap(_pcount,tem._pcount);
	}*/
	
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	T* GetPtr()
	{
		return _ptr;
	}
private:
	T* _ptr;
	int* _pcount;
};

以上一共实现了三种智能指针,其余的想要了解,可以去cplusplus上面看看。

知道了智能指针的种类和实现及其了解,我们几下来就去看看,一些公司是如何对待智能指针的

wKiom1c0k4PinPxsAADufKD5eVU949.png

从上,可看出auto_ptr是被摒弃的,为何呢?

因为一个auto_ptr被拷贝或被赋值后, 其已经失去对原对象的所有权,这个时候,对这个auto_ptr的提领(dereference)操作是不安全的。

int*p=new int(0);
auto_ptr<int>ap1(p);
auto_ptr<int>ap2=ap1;
cout<<*ap1;//错误,此时ap1只剩一个null指针在手了

这种情况较为隐蔽的情形出现在将auto_ptr作为函数参数按值传递,因为在函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的auto_ptr(拷贝构造),这样,传入的实参auto_ptr就失去了其对原对象的所有权,而该对象会在函数退出时被局部auto_ptr删除。

或许大家会想到用auto_ptr的指针或引用作为函数参数或许可以,但是仔细想想,我们并不知道在函数中对传入的auto_ptr做了什么, 如果当中某些操作使其失去了对对象的所有权, 那么这还是可能会导致致命的执行期错误。

这就是为何要摒弃auto_ptr的原因,一句话总结就是:避免潜在的内存崩溃问题。

最后如何选择智能指针?

在掌握了这几种智能指针后,大家可能会想另一个问题:在实际应用中,应使用哪种智能指针呢?

下面给出几个使用指南。


(1)如果程序要使用多个指向同一个对象的指针,应选择shared_ptr。这样的情况包括:


有一个指针数组,并使用一些辅助指针来标示特定的元素,如最大的元素和最小的元素;

两个对象包含都指向第三个对象的指针;

STL容器包含指针。很多STL算法都支持复制和赋值操作,这些操作可用于shared_ptr,但不能用于unique_ptr(编译器发出warning)和auto_ptr(行为不确定)。如果你的编译器没有提供shared_ptr,可使用Boost库提供的shared_ptr。

(2)如果程序不需要多个指向同一个对象的指针,则可使用unique_ptr。如果函数使用new分配内存,并返还指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择。这样,所有权转让给接受返回值的unique_ptr,而该智能指针将负责调用delete。可将unique_ptr存储到STL容器在那个,只要不调用将一个unique_ptr复制或赋给另一个算法(如sort())。