C++智能指针 unique_ptr (scoped_ptr)

首先提出: scoped_ptr是boost库中对这个只能指着的叫法,unique_ptr是C++11标准库中对这个智能指针的叫法

上一篇我们讲解了智能指针中的auto_ptr,但是auto_ptr中还是有很大的缺陷,于是在boost库中,各位民间大佬提出了另外几种智能指针,分别是 scoped_ptr,shared_ptr,还有配合shared_ptr使用的weak_ptr

这一片我们讲解一下scoped_ptr的原理及其用法

scoped_ptr


我们看到,在之前的auto_ptr中最大的缺陷就在于拷贝构造和赋值运算符的重载这两个函数中,所以scoped_ptr就想了一个比较绝对的方法,就是禁止我们使用拷贝构造和赋值运算符的重载

下面给出三种防拷贝的方法,我们观察一下

防拷贝系列1

  • 思想: 将拷贝构造函数和赋值运算符的重载都在类中声明,并给出一个空的定义

template<class T>
class ScopedPtr
{
public:
    ScopedPtr(T* ptr = nullptr):_ptr(ptr)
    {}
    ~ScopedPtr()
    {
        if(_ptr)
        {
            delete _ptr;
            _ptr = nullptr;
        }
    }
    ScopedPtr(const ScopedPtr<T>& ap){}
    ScopedPtr<T>& operator=(const ScopedPtr<T>& ap){}
private:
    T* _ptr;
};

这种方法好吗? 答案是,肯定不好

  • 在类内给出了空定义 -> 这一点很好,完美的防止了在类外这两个函数被别人重新定义

  • 访问限定符为公有 -> 这点就非常的不好了,我们做到的不应该仅仅是防止拷贝,还应该做的是让使用者直到这两个函数是根本不能调用的

防拷贝系列2

  • 思想: 为了解决上面的问题,我们将这两个成员函数设计为私有成员函数

template<class T>
class ScopedPtr
{
private:
    ScopedPtr(const ScopedPtr<T>& ap){}
    ScopedPtr<T>& operator=(const ScopedPtr<T>& ap){}
public:
    ScopedPtr(T* ptr = nullptr):_ptr(ptr)
    {}
    ~ScopedPtr()
    {
        if(_ptr)
        {
            delete _ptr;
            _ptr = nullptr;
        }
    }
private:
    T* _ptr;
};

这个方法好吗? 答案是,比上面的方法优秀了一丢丢~~~

但是但是但是,我们不要忘记了,C++中有一个破坏封装性的利器,就是友元函数,看下面

template<class T>
class ScopedPtr
{
    friend void Test1();
private:
    ScopedPtr(const ScopedPtr<T>& ap){}
    ScopedPtr<T>& operator=(const ScopedPtr<T>& ap){}
public:
    ScopedPtr(T* ptr = nullptr):_ptr(ptr)
    {}
    ~ScopedPtr()
    {
        if(_ptr)
        {
            delete _ptr;
            _ptr = nullptr;
        }
    }
private:
    T* _ptr;
};
void Test1()
{
    ScopedPtr<int> p1(new int);
    ScopedPtr<int> p2(p1);
    p1 = p2;
}

在上面的例子中,Test1()是Scoped_ptr的友元函数,所以可以在Test1()函数中访问类的私有成员,这个时候我们在类外面访问这两个本想防拷贝的函数时,用户也根本就不知道这个函数 不 ! 能 ! 被 ! 拷 ! 贝 !

防拷贝系列3

思想: 将拷贝构造函数和赋值运算符的重载声明为私有的,并且,只声明不定义~

template<class T>
class ScopedPtr
{
private:
    ScopedPtr(const ScopedPtr<T>& ap);
    ScopedPtr<T>& operator=(const ScopedPtr<T>& ap);
public:
    ScopedPtr(T* ptr = nullptr):_ptr(ptr)
    {}
    ~ScopedPtr()
    {
        if(_ptr)
        {
            delete _ptr;
            _ptr = nullptr;
        }
    }
};
  • 将拷贝构造和赋值运算符的重载声明为私有成员 -> 在类外并不可以访问

  • 只声明不定义 -> 是告诉了用户这个函数时不能使用的,因为我们根本就没有实现啊

scoped_array


上面的scoped_ptr存在问题: 对数组无法管理,所以有了 ScopedArray

scoped_array实现起来也是比较简单的,和scoped_ptr基本是一致的

template<class T>
class ScopedArray
{
public:
    ScopedArray(T* ptr = nullptr)
        :_ptr(ptr)
    {
        cout<<"ScopedArray()"<<endl;
    }
    ~ScopedArray()
    {
        cout<<"~ScopedArray()"<<endl;
        if(_ptr)
        {
            delete _ptr;
            _ptr = nullptr;
        }
    }

    // 数组支持随机访问
    T& operator[](size_t index)
    {
        return _ptr[index];
    }

    const T& operator[](size_t index)const
    {
        return _ptr[index];
    }
    
private:
    ScopedArray(const ScopedArray<T>&);
    ScopedArray<T>& operator=(const ScopedArray<T>&);

private:
    T* _ptr;
};

只是比之前新增了对数组的操作,重载了[]运算符

总结一下


  • scoped_ptr实现的最终原理是,将拷贝构造和赋值运算符的重载都声明为私有成员,并且不进行定义,这样在类外我们是无法使用的。

  • 为了解决数组的问题,我们引进了scoped_array,进行有关于数组的智能指针,并且重载了数组相关的操作。

scoped_ptr是boost库中的叫法,在C++11中,标准委员会正式将这个智能指针引入标准库,但是,划重点了,

  • 在C++11中并不叫scoped_ptr,而是unique_ptr!!!

  • C++11实现的unique_ptr以后,并没有去实现unique_array,是因为标准库中有vector,其实是很好用的

猜你喜欢

转载自blog.csdn.net/j4ya_/article/details/81157402