• C++智能指针的简单实现


    1. 简述

    智能指针即使用跟普通指针一样,
    但是拥有自动回收内存的能力。

    2. 简单实现

    利用C++中的局部对象的构造析构特性,实现RAII。

    2.1 整形的智能指针

    简单的int类型的智能指针实现

    class smp
    {
    public:
        explicit smp(int  *q = nullptr):p(q)
        {}
        ~smp()
        {
            delete p;
        }
    private:
        int *p;
    };
    // ... 
    	smp sp(new int(10));
    
        *sp = 5;
        std::cout << *sp << std::endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    2.2 二叉树节点类型的智能指针

    还需要重载->符号,访问数据中的其他成员。

    struct node {
        explicit node(int v = 3):left(nullptr),right(nullptr)
        {}
        struct node *left;
        struct node *right;
        int value;
    };
    class node_smp {
    public:
        explicit node_smp(node *q = nullptr):p(q)
        {}
        ~node_smp()
        {
            delete p;
        }
        node& operator*()
        { return *p;}
        node* operator->()
        { return p;}
    public:
        node *p;
    };
    
      	node_smp nsmp(new node(10));
        nsmp->value = 20;
        std::cout << nsmp->value << std::endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    2.3 通用指针类型
    template<typename T>
    class smart_ptr {
    public:
           explicit smart_ptr(T *p = nullptr):q(p)
           {}
           ~smart_ptr()
           {
               delete q;
               q = nullptr;
           }
    
           T& operator *()
           { return *q;}
    
           T* operator->()
           {
               return q;
           }
    
    
    private:
           T *q;
    };
    
    	smart_ptr<node> smp(new node(10));
        smp->value = 100;
    
        std::cout << smp->value << std::endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    2.4 unique_ptr

    在上面代码的基础上禁止资源的转移,就是unique_ptr的雏形啦。

    template<typename T>
    class unique_ptr_ {
    public:
        explicit unique_ptr_(T *p = nullptr):ptr(p)
        {}
        ~unique_ptr_()
        {
            delete ptr;
        }
        unique_ptr_(const unique_ptr_& p) = delete;
        unique_ptr_& operator=(const unique_ptr_& p) = delete;
    private:
        T *ptr;
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    2.5 引用计数 shared_ptr

    到目前为止,实际上我们并没有处理拷贝构造函数和赋值函数。
    在一个返回内的智能指针,且不会在别的地方被引用。
    也就是该智能指针无法在另外一个地方再使用。

    那么怎么实现能使用多个智能指针对象呢,标准库实现的方式是引用计数。
    而要对多个对象进行计数,且多个对象的销毁并不确定,所以只能在对象首次使用时使用在堆上的分配一个内存进行引用计数。

    当最后一个只能对象销毁时,销毁资源。

    template<typename  T>
    class shared_ptr_ {
    public:
        explicit shared_ptr_(T *data = nullptr):RefCnt(new int {0}) {
            data_ = data;
            *RefCnt = 0;
            if (data_) {
                ++*RefCnt;
            }
        }
        ~shared_ptr_() {
            --*RefCnt;
    
            if (*RefCnt == 0) {
                delete data_;
                delete RefCnt;
            }
        }
        shared_ptr_& operator=(const shared_ptr_ & p) {
    
            if ( *this != p) {
                RefCnt = p.RefCnt;
                data_ = p.data_;
                ++*RefCnt;
            }
    
            return *this;
        }
        shared_ptr_(const shared_ptr_ &p) {
    
                data_ = p.data_;
                RefCnt = p.RefCnt;
                ++*RefCnt;
        }
    
        T& operator*()
        {
            return *data_;
        }
        T* operator->()
        {
            return data_;
        }
    private:
        T *data_;
        int *RefCnt;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    在使用循环链表的过程中即存在这种情况:

    template<typename  T>
    class shared_ptr_ {
    public:
        explicit shared_ptr_(T *data = nullptr):data_(data),RefCnt(new int {0}){
    
            if (data_) {
                ++*RefCnt;
            }
        }
        ~shared_ptr_() {
    
    
            if (*RefCnt) {
                --*RefCnt;
                if ( *RefCnt )
                    std::cout << "current ref: "<< *RefCnt << std::endl;
            }
            if (*RefCnt == 0) {
                delete data_;
                delete RefCnt;
            }
        }
        shared_ptr_<T>& operator=(const shared_ptr_<T> & p)  {
    
            if ( this != &p) {
                RefCnt = p.RefCnt;
                data_ = p.data_;
                if (*RefCnt) {
                    ++*RefCnt;
                }
            }
    
            return *this;
        }
        shared_ptr_(const shared_ptr_ &p) {
    
                data_ = p.data_;
                RefCnt = p.RefCnt;
                if (*RefCnt)
                    ++*RefCnt;
        }
    
        T& operator*()
        {
            return *data_;
        }
        T* operator->()
        {
            return data_;
        }
    private:
        T *data_;
        int *RefCnt;
    };
    
    class List {
    public:
        explicit List(int _v = 0):v(_v), pPre(), pNext()
        {}
        ~List()
        {
            std::cout << "delete List " << v << std::endl;
        }
    
        int v;
        shared_ptr_<List> pPre;
        shared_ptr_<List> pNext;
    };
    
    
    int main()
    {
    
    
    
        shared_ptr_<List> p1(new List(2));
        shared_ptr_<List> p2(new List(3));
       
       
        p1->pPre = p2;
        p2->pPre = p1;
        p1->pNext = p2;
        p2->pNext = p1;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    输出

    current ref: 2
    current ref: 2
    
    • 1
    • 2

    由于实际上在链表中,并不实际占有资源而只是一个引用。所以会出现程序运行后还剩两个计数。
    即对于p1,还有p1->pPrep1->next两个指针计数。

    3. 存在问题

    更好的实现就要看boost和标准库这些的相关实现了。

    • 循环引用未解决
    • 线程安全问题
    • 异常安全
    • 继承

    4. Ref

    code_review
    csdn
    code_project

  • 相关阅读:
    ubuntu下更新vim至最新版本
    如何在.net6webapi中实现自动依赖注入
    华为云Stack的学习(九)
    Linux内核定时器
    Centos7 Yum安装PHP7.2
    上海控安SmartRocket系列产品推介(六):SmartRocket PeneX汽车网络安全测试系统
    .NET周刊【7月第3期 2023-07-16】
    一键分享指标 实现高效的团队协作
    深入理解 Python 虚拟机:字典(dict)的优化
    机器人 Ameca「苏醒」瞬间逼真到令人恐惧,网友纷纷惊叹……
  • 原文地址:https://blog.csdn.net/bdn_nbd/article/details/134462920