02-实现C++智能指针

前言

在第一部分内容中,可以看到如何管理资源。C++祭出的方法是RAII,用资源类来进行管理。但是如何实现这个资源类,却又摆在了面前。在C++原生的库里面支持智能指针auto_ptr,但是这个类在C++17被删除了,取而代之的是shared_ptrunique_ptr这两个从名字上就可以看出来,一个是和原来auto_ptr类似的不存在共享的情况,另一个是允许共享的情况。
既然我们要自己实现一个智能指针,那么应该了解智能指针有些什么功能,也可以称为有什么接口。
来源于C++ Primer Fifth Edition 中文版
这个表格是来自于《C++ primer》,在这里面可以看到需要实现的函数。

  • 空白构造函数
  • 布尔表达式
  • *操作符
  • ->操作符
  • get()函数
  • swap()函数
  • 拷贝构造函数
  • =操作符
  • unique()函数
  • use_count()函数

unique_ptr

对于unique_ptr来说,不允许出现拷贝构造函数和赋值运算符。所以对于这两个函数需要进行禁止。

smart_ptr(const smart_ptr& other) = delete;
smart_ptr(const smart_ptr& rhs) = delete;

下面的介绍主要以shared_ptr 作为核心。

实现模板化

template <typename T>
class smart_ptr {
public:
  explicit smart_ptr(T* ptr = nullptr) : ptr_(ptr) {}
  ~smart_ptr() { delete ptr_; }
  T* get() const { return ptr_; }
private:
  T* ptr_;
};

下面的内容我们就在这个基础框架上进行增加内容了。为了实现布尔表达式,*操作符和->操作符,就需要对这部分操作符进行重载操作。

重载操作符

operator bool() { return ptr_; }
T& operator*() { return *ptr_; }
T* operator->() { return ptr_; }

重载这三个就可以实现*pointpoint->x进行布尔操作
测试函数:

int main() {
    int x = 5;
    shared_wrapper<int> point(&x);
    cout << "*point : " << *point << endl;
    cout << "point-> : " << point.operator->() << endl;
    cout << "point bool : " << (point ? (1) : (2)) << endl;
}

实验结果:
在这里插入图片描述

swap函数

C++在STL库中自带了swap函数,但是我们要命名同名函数就会把标准库函数的STL的作用域覆盖掉,所以就需要使用using这一个关键字将swap作用域重新提出来。

void swap(smart_ptr& rhs) {
	using std::swap;
	swap(ptr_, rhs.ptr_);
}

既然前面可以对这个指针进行操作,所以我们也可以实现。对于两个ptr类的对象进行赋值和拷贝操作。

smart_ptr(smart_ptr& other) {
	ptr_ = other.release();
}
smart_ptr& operator=(smart_ptr& rhs) {
	rhs.swap(*this);
	return *this;
}

大家可能会疑问这个release()是个什么作用的。其实他的作用就是和unique_ptr()相同的功能,释放当前的指针的所有权。这样就把所有权通过返回值的方式释放出去了。

T* release() {
	T *ptr = ptr_;
	ptr_ = nullptr;
	return ptr;
}

既然可以将所有权释放出去,那么就会涉及到拷贝构造函数和移动构造函数。

smart_ptr(smart_ptr&& other) {//移动构造函数
	ptr_ = other.release();
}
smart_ptr(smart_ptr& other) {//拷贝构造函数
	rhs.swap(*this);
	return *this;
}

可以看一下两者的差别,就多了一个&符号而已。那么我们来测试一下。

int main() {
    int x = 4;
    shared_wrapper<int> point_1(&x);
    shared_wrapper<int> point_2(point_1);
    cout << "point_1 : " << *point_1 << endl;
    shared_wrapper<int> point_3(move(point_1));
    cout << "point_1 : " << *point_1 << endl;
}

测试结果
在这里插入图片描述
为什么没有输出最后一行的*point_1呢?,因为通过了移动构造函数过后,当前位置的元素地址已经由另一个指针来进行管理,而原来的指针已经复位为nullptr了。

引用计数

shared_ptr里面对于指向同一区域的指针个数进行了计数,所以我们需要定义一个类来存储计数值。

class count {
public:
	void add_count() { count_++; }
	long reduce_count() { return --count_; }
	long use_count() { return count_; }
	count() : count_(1){}
private:
	long count_;
};

这样就需要修改之前的类定义函数了。

template <typename T>
class shared_wrapper {
public:
    template<typename U>
    friend class shared_wrapper;

    explicit shared_wrapper(T* ptr = nullptr) : ptr_(ptr) {
        if (ptr)
        {
            ptr_count = new shared_count();
        }
        
    }

    ~shared_wrapper(){
        if (ptr_ && !ptr_count->reduce_count())
        {
            delete ptr_count;
            delete ptr_;
        }
    }

    T* get() { return ptr_; }

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

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

    operator bool() const { return ptr_; }

    template <typename U>
    shared_wrapper(shared_wrapper<U>& other ) {
        ptr_ = other.ptr_;
        if (ptr_)
        {
            other.ptr_count->add_count();
            ptr_count = other.ptr_count;
        }    
    }

    template <typename U>
    shared_wrapper(shared_wrapper<U>&& other ) {
        ptr_ = other.ptr_;
        if (ptr_)
        {
            ptr_count = other.ptr_count;
            other.ptr_ = nullptr;
        }
    }

    shared_wrapper& operator= (shared_wrapper rhs) {
        rhs.swap(*this); 
        return *this;
    };

    T* release() {
        T* ptr = ptr_;
        ptr_ = nullptr;
        return ptr;
    }

    void swap(shared_wrapper& rhs) {
        using std::swap;
        swap(ptr_, rhs.ptr_);
        swap(ptr_count, rhs.ptr_count);
    }

    long use_count() const {
        if (ptr_)
        {
            return ptr_count->get_count();
        }
        else
        {
            return 0;
        }
    }
private:
    T* ptr_;
    shared_count *ptr_count;
};

测试函数:

int main() {
    int x = 5;
    shared_wrapper<int> point(&x);
    cout << *point.get() << endl;//5
    cout << *point << endl;//5
    cout << point.use_count() << endl;//1
    shared_wrapper<int> point_2(point);
    cout << point.use_count() << endl;//2
    shared_wrapper<int> point_3(point);
    cout << point.use_count() << endl;//3
    shared_wrapper<int> point_4(move(point_2));
    cout << point.use_count() << endl;//3
}

运行结果:
在这里插入图片描述

发布了16 篇原创文章 · 获赞 13 · 访问量 4283

猜你喜欢

转载自blog.csdn.net/deng821776892/article/details/105615470
今日推荐