智能指针之 Auto_Ptr

在介绍智能指针之前我们先看以下代码:

void FunTest()
{
    int *p = new int[10];
    FILE* pFile = fopen( "1.txt", "w" );
    if (pFile == NULL)
    {
        return;
    }
    // DoSomethint();
    if (p != NULL)
    {
        delete[] p;
        p = NULL;
    }
}

在该代码中,我们注意了对指针的释放却忽略了对文件指针的关闭。C++中的动态内存需要用户自己来维护,动态开辟的空间,在出函数作用域或者程序正常退出前必须释放掉,否则会造成内存泄露,有时我们已经非常谨慎了,然防不胜防。

智能指针(RAII)的存在一开始就是为了解决资源分配即初始化,其解决方案是定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。

所以有了第一代Auto_Ptr

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr = NULL)
        :_ptr(ptr)
    {}

    AutoPtr(AutoPtr<T>& s)
        :_ptr(s._ptr)
    {
        s._ptr = NULL;
        //经过赋值拷贝,原本的智能指针s中_ptr 已经指向空
    }

    AutoPtr<T>& operator=(AutoPtr<T>& s)
    {
        if (this != &s)
        {
            if (_ptr)
            {
                delete _ptr;    
            }   
            _ptr = s._ptr;
            s._ptr = NULL;
            //经过赋值运算符后同样被置空
        }
        return *this;
    }

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

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

    ~AutoPtr()
    {
        if (_ptr)
        {
            delete _ptr;
        }
    }
private:
    T* _ptr;
};


void Funtest1(AutoPtr<char> p)
{}

void Funtest2(AutoPtr<char>& p)
{}


int main()
{
    AutoPtr<char> p1(new char);
    AutoPtr<char> p2(p1);

    //AutoPtr<char> p4(p3);  出错,不能通过编译,原因在拷贝构造函数中更改了具有常性的 p3


    //Funtest1(p2);   同理,以传值形式作为函数形参,也要经过拷贝构造函数形成临时变量p,同理赋值崩溃

    Funtest2(p2);   //以引用的方式传递没有问题
    *p2 = '1';
    return 0;
}

由此可见一代Auto_Ptr的缺点都有以下几点:
1:在Auto_Ptr中经过资源转移后,p1不能再访问空间
2:以值传递的形式作为函数参数后,一旦调用函数,该智能指针在原函数中也不能访问空间
3:const对象不能构造同类型对象

针对第一代Auto_Ptr的问题,于是乎有了第二代Auto_Ptr的产生实际上我们是使用一个bool 类型的 数据成员来表明该智能指针是否具有被析构的能力

template<class T>
class AutoPtr
{
public:
    AutoPtr(T* ptr = NULL)
           :_ptr(ptr)
           , type(false)
    {
        if (_ptr)
            type = true;
    }

    AutoPtr(const AutoPtr<T>& s)
        :_ptr(s._ptr)
        , type(s.type)
    {
        s.type = false;
    }

//拷贝构造函数中,将s对象具有析构的能力 type赋给新对象,而s.type赋值为false,说明不具有析构能力
    AutoPtr<T>& operator=(const AutoPtr<T>& s)
    {
        if (this != &s)
        {
            if (_ptr && type)
            {
                delete _ptr;
            }
            _ptr = s._ptr;
            type = s.type;
            s.type = false;
        }
        return *this;
    }

    T& operator*()const
    {
        return *_ptr;
    }
 //析构函数中,我们将 type 为true的智能指针进行析构,从而避开了对同一空间释放多次的问题
    ~AutoPtr()
    {
        if (_ptr && type)
        {
            delete _ptr;
            _ptr = NULL;
            type = false;
        }
    }

private:
    T* _ptr;
    mutable bool type;
    //在成员函数体内只改变了type的值,针对于const对象,我们加关键字mutable可以更改type的值
};

void Funtest1(AutoPtr<char> p)
{}

int main()
{
    AutoPtr<char> p1(new char);
    void Funtest1(p1);
    AutoPtr<char> p2(p1);
    AutoPtr<char> p3(new char);
    AutoPtr<char> p4(p3);
    Funtest1(p1);
    p1 = p3;
    *p3 ='1';
    return 0;
}

经过验证,我们确定基于第一代的修改我们成功的引入了第二代,但是我们引入了一个野指针的问题,一旦对于野指针进行操作,很容易造成问题,

当我们用智能指针管理一个无名的智能指针对象时,编译器会自动优化,调用系统的构造函数,如果我们想调用我们自己写的构造函数,我们推导出智能指针的第三个版本

template<class T>
class autoptrref
{
public:
    autoptrref(T* ptr = NULL)
        :_ptr(ptr)
    {}
    T* _ptr;
};

template<class T>
class autoptr
{
public:
    //构造函数
    autoptr(T* ptr = NULL)
        :_ptr(ptr)
    {}

    //拷贝构造函数
    autoptr(autoptr<T>& ap)
        :_ptr(ap._ptr)
    {
        ap._ptr = NULL;//拷贝完后将ap._ptr置NULL
    }

    //赋值运算符的重载
    autoptr<int>& operator=(autoptr<T>& ap)
    {
        if (this != &ap)
        {
            if (_ptr != NULL)
            {
                delete _ptr;
            }
            _ptr = ap._ptr;
            ap._ptr = NULL;//赋值完后将ap._ptr置NULL
        }
        return *this;
    }

    //解决办法

    //重载autoptrref()
    operator autoptrref<T>()//将autoptr<T>转化为autoptrref<T>
    {
        autoptrref<T> temp(_ptr);
        _ptr = NULL;
        return temp;
    }

    //再重载一个拷贝构造函数
    autoptr(const autoptrref<T>& apr)
        :_ptr(apr._ptr)
    {}

    ~autoptr()//析构函数
    {
        if (_ptr != NULL)
        {
            delete _ptr;
        }
    }


    //*的重载
    T& operator*()const
    {
        return *_ptr;
    }

    //得到原生态指针
    T* Get()const
    {
        return _ptr;
    }

private:
    T* _ptr;
};
void funtest()
{
    autoptr<int> ap(autoptrref<int>(autoptr<int>(new int)));
    //用无名的智能指针拷贝构造智能指针ap,则不会调用我们编写好得拷贝构造函数,而是调用系统自动合成个的拷贝构造函数
}    //原因是编译器的自动优化,解决办法:重新增加一个类autoptrref,将无名对象的类型转化为autoptrref                                       

int main()
{
    funtest();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值