C++之共享智能指针shared_ptr的实现

目录

概念

使用标准库共享智能指针

共享智能指针底层实现


概念

共享指针是可以  由多个栈上智能指针对象  同时托管同一堆上资源的。


因为shared_ptr的内部实现引用计数来管理有多少智能指针指向了这块堆上资源。


当一个共享智能指针出栈时,引用计数会自动-1。当最后一个共享智能指针出栈引用计数为0时,代表没有智能指针对象托管这块堆上资源了,此时将执行释放对上资源的操作
 

使用标准库共享智能指针

        引入头文件:#include <memory>

        创建Stu类:

#include <iostream>
#include <memory>
using namespace std;

class Stu
{
private:
    string name;
    int age;
public:
    Stu(string name, int age)
    {
        this->name = name;
        this->age = age;
        cout << "Stu的构造" << endl;
    }
    void showInfo()
    {
        cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
    }
    ~Stu()
    {
        cout << "Stu的析构" << endl;
    }
};

        使用标准库智能指针:

int main()
{
    //使用系统方法
    shared_ptr<Stu> ptr1 (new Stu("zhangsan", 20));
    ptr1->showInfo();
    cout << "ptr1.count=" << ptr1.use_count() << endl;  //>>ptr1.count=1
    shared_ptr<Stu> ptr2 = ptr1;    //拷贝构造创建新共享指针
    shared_ptr<Stu> ptr3;
    ptr3 = ptr2;                    //=号赋值创建新共享指针

    ptr1->showInfo();
    cout << "ptr1.count=" << ptr1.use_count() << endl;  //>>ptr1.count=3

    ptr2->showInfo();
    cout << "ptr2.count=" << ptr1.use_count() << endl;  //>>ptr2.count=3

    ptr3->showInfo();
    cout << "ptr3.count=" << ptr1.use_count() << endl;  //>>ptr1.count=3
    //shared_ptr是共享智能指针,所以三个ptr打印内容相同.
    cout << "---------------" << endl;


    //析构一个共享智能指针,看看count变化
    ptr1.~shared_ptr();
    cout << "ptr3.count=" << ptr3.use_count() << endl;    //>>ptr3.count=2
    cout << "ptr4.count=" << ptr1.use_count() << endl;    //>>ptr4.count=2
    cout << sizeof (ptr3) << endl;      //16,是两个指针的大小
    cout << sizeof (ptr1) << endl;      //16

    cout << "-------------------------" << endl;

    return 0;
}

        运行结果:

        

共享智能指针底层实现

        先要理解共享智能指针的实质。

        共享智能指针实际上是由两个类组成:一个计数器类,一个指针类。

         计数器类(代码片段):

template <class T>
class RefCount
{
private:
    T * ptr;        //用来指向堆区Stu对象
    int count;      //用来计数

        指针类(代码片段):

template<class T>
class SHARED_ptr
{
private:
    T * ptr;        //用来指向堆区Stu对象
    RefCount<T> * refcount; //类中嵌套 类的指针, 用来指向计数器类

                即指针类中包含计数器类的指针。

        当多个共享智能指针指向同一片空间时,实际上是由多个指针类和一个计数器类共同指向该堆区空间。

        所以改变计数器类的值,所有通过指针类查看的计数器值都是一样的。

        自定义底层实现时,需注意:在拷贝构造、=号赋值时,对指针类中计数器指针的处理,并非时将外类计数器的值复制给本类计数器的值,而是将本类计数器指针,指向外类计数器指针地址,达到共用同一个计数的目的。

        

        自己实现shared_ptr类,需两部分,计数器类和指针类,Stu类仍用上边的。

        自定义计数器类代码:

//计数器类
template <class T>
class RefCount
{
private:
    T * ptr;        //用来指向堆区Stu对象
    int count;      //用来计数
public:
    //构造函数
    RefCount(T* ptr = nullptr)
    {
        this->ptr = ptr;
        if(this->ptr != nullptr)
        {
            this->count = 1;
        }
        else{
            this->count = 0;
        }
    }

    //增加引用计数
    void addRef()
    {
        this->count++;
    }

    //减少引用计数
    int delRef()
    {
        //这里把计数器返回出去的目的是,
        //当减完后,可直接判断计数器,如果为0,就直接释放空间
        //省了一步get_count
        return --this->count;
    }

    //获取引用计数
    int get_count()
    {
        return this->count;
    }

};

        自定义指针类:

//SHARED_ptr的类模板
template<class T>
class SHARED_ptr
{
private:
    T * ptr;        //用来指向堆区Stu对象
    RefCount<T> * refcount; //类中嵌套 类的指针, 用来指向计数器类
public:
    //SHARED_ptr的构造
    SHARED_ptr(T * ptr = nullptr)
    {
        this->ptr = ptr;
        if(this->ptr != nullptr)
        {
            this->refcount = new RefCount<T>(this->ptr);
        }
        else{
            this->refcount = new RefCount<T>();
        }
    }

    //SHARED_ptr的析构
    ~SHARED_ptr()
    {
        //每个对象析构的时候,都让计数器--,并判断计数器是否为0,
        //如果计数器为0了,说明析构的是最后一个对象,那么就释放空间
        if(this->refcount->delRef() == 0){
            delete ptr;
            delete refcount;
            this->ptr = nullptr;
            this->refcount = nullptr;
        }
    }

    //SHARED_ptr的拷贝构造
    SHARED_ptr(const SHARED_ptr& other)
    {
        //如果外类不为空
        if(other.ptr != nullptr)
        {
            //将外类堆地址赋值给本类的指针
            this->ptr = other.ptr;
            
            //错误理解:将外类的计数器,赋值给本类计数器<<--这种理解是错误的
            //正确理解:将本类的计数器指针,也指向外类计数器的地址
            this->refcount = other.refcount;

            //拷贝构造调用时机是在初始化类对象时,所以本类计数器+1
            this->refcount->addRef();

        }
        //如果外类为空
        else {
        this->ptr = other.ptr;  //将外类堆地址赋值给本类的指针
        this->refcount = other.refcount;    //将外类的计数器,赋值给本类计数器
        }
    }

    //SHARED_ptr的=号运算符重载
    SHARED_ptr& operator = (const SHARED_ptr& other)
    {
        if(this == &other)
        {
            return  *this;
        }

        //外类为空
        if(other.ptr == nullptr)
        {
            //外类空,本类非空
            if(this->ptr != nullptr)
            {
                //本类计数器-1,如果减后计数器为0,
                //说明堆空间没人用了,该回收了
                if(this->refcount->delRef() == 0)
                {
                    delete this->ptr;
                    delete this->refcount;
                    this->ptr = nullptr;
                    this->refcount = nullptr;
                }
            }
            //外类赋值给本类
            this->ptr = other.ptr;
            this->refcount = other.refcount;
        }
        //外类非空
        else {
            //外类非空,本类非空
            if(this->ptr != nullptr)
            {
                //两个类都非空,先对本类进行计数器-1,
                //如果计数器为0了,那就释放堆区资源
                if(this->refcount->delRef() == 0)
                {
                    delete this->ptr;
                    delete this->refcount;
                    this->ptr = nullptr;
                    this->refcount = nullptr;
                }
            }

            //然后外类对本类赋值
            this->ptr = other.ptr;
            this->refcount = other.refcount;

            //赋值后,本类计数器需要+1
            this->refcount->addRef();
        }

        return  *this;
    }

    //取*运算符重载
    T& operator*()
    {
        return *ptr;
    }

    //get方法
    T* get()
    {
        return ptr;
    }

    //指针运算符重载
    T* operator->()
    {
        return ptr;
    }

    //获取引用记数
    int use_count()
    {
        return refcount->get_count();
    }

};

        效果实现:

int main()
{
    //使用自定义共享智能指针
    SHARED_ptr<Stu> ptr4 (new Stu("zhangsan", 20));
    ptr4->showInfo();
    cout << "ptr4.count=" << ptr4.use_count() << endl;    //>>ptr4.count=1

    SHARED_ptr<Stu> ptr5 = ptr4;
    SHARED_ptr<Stu> ptr6;
    ptr6 = ptr5;

    ptr4->showInfo();
    cout << "ptr4.count=" << ptr4.use_count() << endl;  //>>ptr4.count=3

    ptr5->showInfo();
    cout << "ptr5.count=" << ptr5.use_count() << endl;  //>>ptr5.count=3

    ptr6->showInfo();
    cout << "ptr6.count=" << ptr6.use_count() << endl;  //>>ptr6.count=3
    cout << "----------------------------" << endl;

    //析构一个智能指针,看看count变化
    ptr4.~SHARED_ptr();
    cout << "ptr6.count=" << ptr6.use_count() << endl;    //>>ptr6.count=2
    cout << "ptr4.count=" << ptr4.use_count() << endl;    //>>ptr4.count=2
    cout << sizeof (ptr6) << endl;      //16,是两个指针的大小
    cout << sizeof (ptr4) << endl;      //16

    return 0;
}

        运行结果:

        

        再次强调易错误区:

        误区: 本类计数器+1了,但是其他共享智能指针的计数呢?
        解答: 共享智能指针由 计数器类 和 SHARED_ptr类 两部分组成,,多个共享智能指针指向同一片空间,实际上是一个计数器对象和多个SHARED_ptr对象。 每次的赋值操作,都是让SHARED_ptr中的计数器指针,指向该唯一的计数器类。所以,只让本类的计数器+1,所有的共享智能指针都会更新。

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值