• 【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());
        }
    };

      与君共勉!

  • 相关阅读:
    SonarQube系列三、Jenkins集成SonarQube(dotnetcore篇)
    SonarQube系列二、分析dotnet core/C#代码
    SonarQube系列一、Linux安装与部署
    asp.net core 集成JWT(二)token的强制失效,基于策略模式细化api权限
    asp.net core 集成JWT(一)
    visual studio 各版本激活码
    服务网关Ocelot 入门Demo系列(01-Ocelot极简单Demo及负载均衡的配置)
    一人撸PaaS之“应用”
    使用Roslyn脚本化C#代码,C#动态脚本实现方案
    try.dot.net 的正确使用姿势
  • 原文地址:https://www.cnblogs.com/qq329914874/p/6653618.html
Copyright © 2020-2023  润新知