【C++】智能指针简述(二):auto_ptr

  首先,我要声明auto_ptr是一个坑!auto_ptr是一个坑!auto_ptr是一个坑!重要的事情说三遍!!!

  通过上文,我们知道智能指针通过对象管理指针,在构造对象时完成资源的分配及初始化,在析构对象时完成资源的清理及汕尾工作.

  因此,可以得到一份简洁版的智能指针代码:

template<typename T>
class AutoPtr{
public:
	//构造函数,完成资源的初始化与分配
	AutoPtr(T * ptr = NULL)
		:_ptr(ptr){}
	//析构函数,完成资源的清理及汕尾工作
	~AutoPtr(){
		if(_ptr!=NULL){
			delete _ptr;
			_ptr = NULL;
		}
	}
private:
	T *_ptr;
};

  大致一看,没毛病!突然觉得自己无所不能,感觉自己就是传说中的编程天才!

  可是,如果我想这样的话.....:

AutoPtr<int> ap1(new int(100));
AutoPtr<int> ap2(new int(200));
AutoPtr<int> ap3(ap1);
AutoPtr<int> ap4();
ap4 = ap2;

  ap1与ap3共同管理一块空间;ap2与ap4共同管理一块空间,看起来好像没什么问题.

  但当程序跑起来,出了函数作用域之后....崩毁了!!?

  ......Why?

  好在我经验丰富,见多识广,脑袋回路中很自然地想起了类似的情况:string类的浅拷贝....

  因此,机智的我立刻发现了原因:由于没有定义拷贝构造函数与赋值运算符重载,那么在拷贝构造对象和给对象赋值时,系统会默认生成相应函数.

  ap1与ap3共同管理一块空间,一旦出了函数作用域,ap3会调用析构函数,delete掉所指向的空间;

  而当ap1调用析构函数时,此时ap1所指向的已经是一块非法内存(因为被ap3 delete过了),因此当ap1再次delete这块空间时,程序挂掉了!

  简而言之:同样一块空间被delete了两次,所以最终程序挂掉了!

  那么我就好奇了,auto_ptr如何应对拷贝与赋值的呢?

  在百度了各种资料及阅读其源代码之后,发现auto_ptr是这么处理的:

//拷贝构造
AutoPtr(AutoPtr& ap){
	//转移管理权
	_ptr = ap._ptr;
	ap._ptr = NULL;
}
//赋值运算符重载
AutoPtr &operator=(AutoPtr &ap){
	if(ap._ptr != _ptr){
		AutoPtr tmp(ap);
		std::swap(_ptr,tmp._ptr);
	}//由析构函数去管理tmp
	return *this;
}

  这是我简化后的代码,再次应对上述情况时:

AutoPtr<int> ap1(new int(100));
AutoPtr<int> ap2(new int(200));
AutoPtr<int> ap3(ap1);  //ap3 =  NULL
AutoPtr<int> ap4();
ap4 = ap2;          //ap2 = NULL

  我们发现:auto_ptr通过转移管理权,来保证在赋值与拷贝时仅管理一份指针,而防止同一块空间释放多次的问题.

  最后,我将自己写的简洁、精简、易读的AutoPtr与库中的代码一起贴上来

/*
*文件说明:智能指针之AutoPtr
*作者:高小调
*日期:2017-03-30
*集成开发环境:Microsoft Visual Studio 2010
*Github:https://github.com/gaoxiaodiao/c_cplusplus/blob/master/SmartPointer/AutoPtr.h
*/
#pragma once
template<typename T>
class AutoPtr{
public:
	//构造函数
	AutoPtr(T * ptr = NULL)
		:_ptr(ptr){}
	//拷贝构造
	AutoPtr(AutoPtr& ap){
		//转移管理权
		_ptr = ap._ptr;
		ap._ptr = NULL;
	}
	//赋值运算符重载
	AutoPtr &operator=(AutoPtr &ap){
		if(ap._ptr != _ptr){
			AutoPtr tmp(ap);
			std::swap(_ptr,tmp._ptr);
		}//由析构函数去管理tmp
		return *this;
	}
	//析构函数
	~AutoPtr(){
		if(_ptr!=NULL){
			delete _ptr;
			_ptr = NULL;
		}
	}
private:
	T *_ptr;
};

void TestAutoPtr(){
	AutoPtr<int> ap1(new int(100));
	AutoPtr<int> ap2(new int(200));
	AutoPtr<int> ap3(ap1);
	AutoPtr<int> ap4(ap2);
	ap3 = ap4;
}

  库内实现(我就懒得写注释了,看完精简版后,再看库中实现会发现库内的封装性、代码复用性更高一些)

template<class T>
class auto_ptr
{
private:
    T*ap;
public:
    //constructor & destructor-----------------------------------(1)
    explicit auto_ptr(T*ptr=0)throw():ap(ptr)
    {
    }
 
    ~auto_ptr()throw()
    {
        delete ap;
    }
    //Copy & assignment--------------------------------------------(2)
    auto_ptr(auto_ptr& rhs)throw():ap(rhs.release())
    {
    }
    template<class Y>
    auto_ptr(auto_ptr<Y>&rhs)throw():ap(rhs.release())
    {
    }
    auto_ptr& operator=(auto_ptr&rhs)throw()
    {
        reset(rhs.release());
        return *this;
    }
    template<class Y>
    auto_ptr& operator=(auto_ptr<Y>&rhs)throw()
    {
        reset(rhs.release());
        return *this;
    }
    //Dereference----------------------------------------------------(3)
    T& operator*()const throw()
    {
        return *ap;
    }
    T* operator->()const throw()
    {
        return ap;
    }
    //Helper functions------------------------------------------------(4)
    //value access
    T* get()const throw()
    {
        return ap;
    }
    //release owner ship
    T* release()throw()
    {
        T*tmp(ap);
        ap=0;
        return tmp;
    }
    //reset value
    void reset(T*ptr=0)throw()
    {
        if(ap!=ptr)
        {
            deleteap;
            ap=ptr;
        }
    }
    //Special conversions-----------------------------------------------(5)
    template<class Y>
    struct auto_ptr_ref
    {
        Y*yp;
        auto_ptr_ref(Y*rhs):yp(rhs){}
    };
    auto_ptr(auto_ptr_ref<T>rhs)throw():ap(rhs.yp)
    {
    }
     
    auto_ptr& operator=(auto_ptr_ref<T>rhs)throw()
    {
        reset(rhs.yp);
        return*this;
    }
     
    template<class Y>
    operator auto_ptr_ref<Y>()throw()
    {
        returnauto_ptr_ref<Y>(release());
    }
     
    template<class Y>
    operator auto_ptr<Y>()throw()
    {
        returnauto_ptr<Y>(release());
    }
};

  与君共勉!

posted @ 2017-03-31 21:26  C/C++专题  阅读(932)  评论(0编辑  收藏  举报