• 自己动手构造一个shared_ptr (未完待续)


    自己动手构造一个shared_ptr(未完待续)

    1. shared_ptr 功能 & 特点

    shared_ptr是一种智能指针(smart pointer),作用有如同指针,但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。

    shared_ptrC++11提供的一种智能指针类,它足够智能,可以在任何地方都不使用时自动删除相关指针,从而帮助彻底消除内存泄漏和悬空指针的问题。它遵循共享所有权的概念,即不同的 shared_ptr 对象可以与相同的指针相关联,并在内部使用引用计数机制来实现这一点。

    2. 明确需求

    • 指针:因为本身shared_ptr就是封装一个指针的功能;

    • 引用计数:要求不同的shared_ptr对象都可以访问同一个ref_cnt (reference count);

    • 构造函数:指针指向,ref_cnt赋值 / 修改;

    • 析构函数:引用计数 - 1,

      • 如果引用计数 == 0,需要delete 引用计数和指针。
    • 可以输出当前引用计数 (use_count()

    • 可以自定义删除器;

    • 未完待续…

    3. 开写

    3.1 构建类,使用模板,创建构造函数

    使用类名称my_shared_ptr,创建构造函数和析构函数。

    • 指针:创建my_shared_ptr的指针,这里用的是T* my_ptr;
    • 引用计数:定义引用计数ref_cnt,注意这里用的是指针(而不是普通变量),为了不同的my_shared_ptr都可以访问。
    • 构造函数,指针指向,ref_cnt赋值 / 修改。
    class my_shared_ptr {
    public:
        my_shared_ptr(T* to_be_pointed) {
            my_ptr = to_be_pointed;
            ref_cnt = new int(1);
        }
    
        my_shared_ptr(const my_shared_ptr& origin_ptr) {
            my_ptr = origin_ptr.my_ptr;
            ref_cnt = origin_ptr.ref_cnt;
            ++(*ref_cnt);
        }
        
        ~my_shared_ptr() {}
    private:
        T* my_ptr;
        int* ref_cnt; // reference count pointer
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.2 析构函数

    • 析构函数:引用计数 - 1,
      • 如果引用计数 == 0,需要delete 引用计数和指针。
    ~my_shared_ptr() {
        --(*ref_cnt);
        if ((*ref_cnt) == 0) {
    
            // 释放内存空间
            delete my_ptr;
            delete ref_cnt;
    
            // 避免野指针产生
            my_ptr = nullptr;
            ref_cnt = nullptr;
    
            cout << "real destructed" << endl;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其实我这里定义的是默认的销毁,但是可能if (*(ref_cnt) == 0) {deleter();}不过在构造的时候要传入自定义的deleter()函数的地址。举例:给shared_ptr添加自定义删除器

    3.3 use_count

    • 能输出当前引用计数👇
    int used_count() {
    	return *(this -> ref_cnt); 
    }
    
    • 1
    • 2
    • 3

    3.4 自定义删除器

    • 这里的含义就是自定义销毁程序,因为传入的类不一定可以直接使用delete完美消除,因此可以这样写👇

    首先在类内定义函数指针👇

    void (*my_deleter)(T* p); // my deleter
    
    • 1

    自定义销毁函数👇(我这里直接定义成int类型)

    // 这里的deleter是针对某种类型的p自定义删除函数
    void deleter(int* p) {
        delete p;
        p = nullptr;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    创建相应的构造函数(传入函数指针,并保存在类内函数指针中)👇

    my_shared_ptr(const my_shared_ptr& origin_ptr, void (*input_my_deleter)(int *p)) {
        my_ptr = origin_ptr.my_ptr;
        ref_cnt = origin_ptr.ref_cnt;
        ++(*ref_cnt);
    
        my_deleter = input_my_deleter;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    析构函数做出相应改变👇(就是将delete my_ptr;改成调用my_deleter(my_ptr);

    ~my_shared_ptr() {
        --(*ref_cnt);
        if ((*ref_cnt) == 0 && my_deleter == nullptr) {
    
            // 释放内存空间
            delete my_ptr;
            delete ref_cnt;
    
            // 避免野指针产生
            my_ptr = nullptr;
            ref_cnt = nullptr;
    
            cout << "real destructed (ref_cnt = 0)" << endl;
        } else if ((*ref_cnt) == 0 && my_deleter) {
            my_deleter(my_ptr);
    
            delete ref_cnt;
            ref_cnt = nullptr;
    
    		cout << "real destructed (ref_cnt = 0)" << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4. 完整测试程序

    4.1 cpp代码👇(具体实现含义已经在前面讲过)

    #include 
    using namespace std; 
    template<class T>
    
    // void deleter(int* p);
    
    class my_shared_ptr {
    public:
        my_shared_ptr(T* to_be_pointed) {
            my_ptr = to_be_pointed;
            ref_cnt = new int(1);
        }
    
        my_shared_ptr(const my_shared_ptr& origin_ptr) {
            my_ptr = origin_ptr.my_ptr;
            ref_cnt = origin_ptr.ref_cnt;
            ++(*ref_cnt);
        }
    
        my_shared_ptr(const my_shared_ptr& origin_ptr, void (*input_my_deleter)(int *p)) {
            my_ptr = origin_ptr.my_ptr;
            ref_cnt = origin_ptr.ref_cnt;
            ++(*ref_cnt);
    
            my_deleter = input_my_deleter;
        }
    
        int used_count() {
            if (ref_cnt != nullptr) {
                return *(this -> ref_cnt); 
            } else {
                return 0;
            }
        }
    
        ~my_shared_ptr() {
            --(*ref_cnt);
            if ((*ref_cnt) == 0 && my_deleter == nullptr) {
                
                // 释放内存空间
                delete my_ptr;
                delete ref_cnt;
    
                // 避免野指针产生
                my_ptr = nullptr;
                ref_cnt = nullptr;
    
                cout << "real destructed (ref_cnt = 0)" << endl;
            } else if ((*ref_cnt) == 0 && my_deleter) {
                my_deleter(my_ptr);
                
                delete ref_cnt;
                ref_cnt = nullptr;
    
                cout << "real destructed (ref_cnt = 0)" << endl;
            }
        }
    private:
        T* my_ptr;
        int* ref_cnt; // reference count pointer
        void (*my_deleter)(T* p); // my deleter
    };
    
    // 这里的deleter是针对某种类型的p自定义删除函数
    void deleter(int* p) {
        delete p;
        p = nullptr;
    }
    
    
    int main() {
        int* p = new int(123);
        my_shared_ptr<int> p1(p);
        std::cout << "use_count: " << p1.used_count() << std::endl;
        my_shared_ptr<int> p2(p1);
        std::cout << "use_count: " << p1.used_count() << std::endl;
        my_shared_ptr<int> p3(p2, deleter);
        std::cout << "use_count: " << p1.used_count() << std::endl;
    
        p1.~my_shared_ptr();
        std::cout << "use_count: " << p3.used_count() << std::endl;
        p2.~my_shared_ptr();
        std::cout << "use_count: " << p3.used_count() << std::endl;
        p3.~my_shared_ptr();
    
        exit(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
    • 86
    • 87

    4.2 Linux下运行命令

    上述代码保存在my_shared_ptr.cpp

    编译👇

    g++ my_shared_ptr.cpp -o my_shared_ptr
    
    • 1

    运行👇

    ./my_shared_ptr 
    
    • 1

    4.3 运行结果

    levi@LEVI1:~/code$ g++ my_shared_ptr.cpp -o my_shared_ptr
    levi@LEVI1:~/code$ ./my_shared_ptr 
    use_count: 1
    use_count: 2
    use_count: 3
    use_count: 2
    use_count: 1
    real destructed (ref_cnt = 0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    未完待续…

    可能还需要加入的有:weak ptr的协同;运算符的重载(比如=)。

  • 相关阅读:
    docker 安装 Sql Server
    深入剖析JavaScript(一)——函数式编程
    阿里架构师深度学习java虚拟机心得PDF、不愧是进了阿里的大牛
    OpenGL -文字
    基于单片机的显示系统
    【数据结构】时间复杂度和空间复杂度
    安装Ubuntu提示:系统找不到指定的文件。
    MSP430F5529库函数定时器A——定时中断
    带你吃透Servlet技术(一)
    阿里云 linux 的nginx 配置uni-app的H5前端项目vue,后端接口阿里云。
  • 原文地址:https://blog.csdn.net/ahundredmile/article/details/126546452