• C++ 11 智能指针


    智能指针

    目录

    智能指针

    共享的智能指针shared_ptr

    Abstract

    How

    Demo

    独占的智能指针unique_ptr

    Abstract

    How

    Demo

    弱引用的智能指针weak_ptr

    Abstract

    How

    Demo

    shared_ptr 源码分析

    gcc版本源码分析图

    分析


    在C++中没有垃圾回收机制,必须自己释放分配的内存,否则就会造成内存泄露。解决这个问题最有效的方法是使用智能指针(smart pointer)。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存。

    C++11中提供了三种智能指针,使用这些智能指针时需要引用头文件

    std::shared_ptr:共享的智能指针

    std::unique_ptr:独占的智能指针

    std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的。

    共享的智能指针shared_ptr

    Abstract

    shared_ptr使用了引用计数,每一个shared_ptr的拷贝都指向相同的内存,每次拷贝都会触发引用计数+1,每次生命周期结束析构的时候引用计数-1,在最后一个shared_ptr析构的时候,内存才会释放。

    How

    1. shared_ptr的初始化

    共享智能指针是指多个智能指针可以同时管理同一块有效的内存,共享智能指针shared_ptr 是一个模板类,如果要进行初始化有三种方式:

    通过构造函数;

    std::make_shared辅助函数;

    reset方法。

    共享智能指针对象初始化完毕之后就指向了要管理的那块堆内存,如果想要查看当前有多少个智能指针同时管理着这块内存可以使用共享智能指针提供的一个成员函数use_count

    2.获取原始指针

    对应基础数据类型来说,通过操作智能指针和操作智能指针管理的内存效果是一样的,可以直接完成数据的读写。但是如果共享智能指针管理的是一个对象,那么就需要取出原始内存的地址再操作,可以调用共享智能指针类提供的get()方法得到原始地址

    3. 指定删除器

    当智能指针管理的内存对应的引用计数变为0的时候,这块内存就会被智能指针析构掉了。另外,我们在初始化智能指针的时候也可以自己指定删除动作,这个删除操作对应的函数被称之为删除器,这个删除器函数本质是一个回调函数,我们只需要进行实现,其调用是由智能指针完成的。

    4. 注意事项

    • 不要用一个裸指针初始化多个shared_ptr,会出现double_free导致程序崩溃
    • 通过shared_from_this()返回this指针,不要把this指针作为shared_ptr返回出来,因为this指针本质就是裸指针,通过this返回可能 会导致重复析构,不能把this指针交给智能指针管理。
    • 尽量使用make_shared,少用new。
    • 不要delete get()返回来的裸指针。
    • 不是new出来的空间要自定义删除器。
    • 要避免循环引用,循环引用导致内存永远不会被释放,造成内存泄漏。

    Demo

    使用

    #include 
    using namespace std;
    #include 
    #include 
     
    class Test
    {
    public:
        Test() : m_num(0)
        {
            cout << "construct Test..." << endl;
        }
     
        Test(int x) : m_num(0)
        {
            cout << "construct Test, x = " << x << endl;
        }
     
        Test(string str) : m_num(0)
        {
            cout << "construct Test, str = " << str << endl;
        }
     
        ~Test()
        {
            cout << "destruct Test..." << endl;
        }
     
        void setValue(int v)
        {
            this->m_num = v;
        }
     
        void print()
        {
            cout << "m_num: " << this->m_num << endl;
        }
     
    private:
        int m_num;
    };
     
    int main()
    {
        /*--------------------------  一,初始化智能指针shared_ptr  ------------------------------*/
        //1.通过构造函数初始化
        shared_ptr ptr1(new int(3));
        cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl;
     
        //2.通过移动和拷贝构造函数初始化
        shared_ptr ptr2 = move(ptr1);
        cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl;
        cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
     
        shared_ptr ptr3 = ptr2;
        cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl;
        cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl;
     
        //3.通过 std::make_shared初始化
        shared_ptr ptr4 = make_shared(8);
        shared_ptr ptr5 = make_shared(7);
        shared_ptr ptr6 = make_shared("GOOD LUCKLY!");
     
        //4.通过reset初始化
        ptr6.reset(); //重置ptr6, ptr6的引用基数为0
        cout << "ptr6管理的内存引用计数: " << ptr6.use_count() << endl;
     
        ptr5.reset(new Test("hello"));
        cout << "ptr5管理的内存引用计数: " << ptr5.use_count() << endl;
     
        cout << endl;
        cout << endl;
     
        /*--------------------------  二,共享智能指针shared_ptr的使用  ------------------------------*/
        //1.方法一
        Test* t = ptr5.get();
        t->setValue(1000);
        t->print();
     
        //2.方法二
        ptr5->setValue(7777);
        ptr5->print();
     
        printf("\n\n");
        /*------------------------------------  三,指定删除器  -----------------------------------*/
         //1.简单举例
        shared_ptr ppp(new Test(100), [](Test* t) {
            //释放内存
            cout << "Test对象的内存被释放了......." << endl;
            delete t;
            });
        printf("----------------------------------------------------------------------\n");
     
        2.如果是数组类型的地址,就需要自己写指定删除器,否则内存无法全部释放
        //shared_ptr p1(new Test[5], [](Test* t) {
        //    delete[]t;
        //    });
     
        //3.也可以使用c++给我们提供的 默认删除器函数(函数模板)
        shared_ptr p2(new Test[3], default_delete());
     
        //4.c++11以后可以这样写 也可以自动释放内存
        shared_ptr p3(new Test[3]);
     
        return 0;
    }

    数组模板

    #include 
    #include 
    #include 
    using namespace std;
     
    //有了这个函数模板,我们就不用自己去释放数组类型的地址了
    template 
    shared_ptr make_share_array(size_t size) 
    {
    	//返回匿名对象
    	return shared_ptr(new T[size], default_delete());
    }
     
    int main()
    {
    	shared_ptr ptr1 = make_share_array(10);
    	cout << ptr1.use_count() << endl;
     
    	shared_ptr ptr2 = make_share_array(7);
    	cout << ptr2.use_count() << endl;
    		
    	return 0;
    }

    独占的智能指针unique_ptr

    Abstract

    std::unique_ptr是一个独占型的智能指针,它不允许其它智能指针共享其内部指针,也不允许unique_ptr的拷贝和赋值。使用方法和shared_ptr类似,区别是不可以拷贝。

    从哲学的角度讲,unique 与share 实现内存安全的思路完全相反,一个是通过不允许多份内存指针,一个是对内存指针的使用进行计数。

    How

    1. 初始化

    std::unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,可以通过它的构造函数初始化一个独占智能指针对象,但是不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。

    2. 删除器

    unique_ptr指定删除器和shared_ptr指定删除器是有区别的,unique_ptr指定删除器的时候需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器

    Demo

    using namespace std;
    
    struct A {
        ~A() {
            cout << "A delete" << endl;
        }
        void Print() {
            cout << "A" << endl;
        }
    };
    
    
    int main() {
        auto ptr = std::unique_ptr(new A);
        auto tptr = std::make_unique(); // error, c++11还不行,需要c++14
        std::unique_ptr tem = ptr; // error, unique_ptr不允许移动
        ptr->Print();
        return 0;
    }

    弱引用的智能指针weak_ptr

    Abstract

    weak_ptr的思路就是观察者模式,是用来监视shared_ptr的生命周期,std::weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视shared_ptr中管理的资源是否存在,可以用来返回this指针和解决循环引用问题。

    How

    1. 初始化

    #include 
    #include 
    using namespace std;
     
    int main() 
    {
        shared_ptr sp(new int);
     
        weak_ptr wp1;
        weak_ptr wp2(wp1);
        weak_ptr wp3(sp);
        weak_ptr wp4;
        wp4 = sp;
        weak_ptr wp5;
        wp5 = wp3;
        
        return 0;
    }

    weak_ptr wp1;构造了一个空weak_ptr对象

    weak_ptr wp2(wp1);通过一个空weak_ptr对象构造了另一个空weak_ptr对象

    weak_ptr wp3(sp);通过一个shared_ptr对象构造了一个可用的weak_ptr实例对象

    wp4 = sp;通过一个shared_ptr对象构造了一个可用的weak_ptr实例对象(这是一个隐式类型转换)

    wp5 = wp3;通过一个weak_ptr对象构造了一个可用的weak_ptr实例对象

    通过调用std::weak_ptr类提供的use_count()方法可以获得当前所观测资源的引用计数

    2. 常用函数

    通过调用std::weak_ptr类提供的expired()方法来判断观测的资源是否已经被释放

    通过调用std::weak_ptr类提供的lock()方法来获取管理所监测资源的shared_ptr对象

    通过调用std::weak_ptr类提供的reset()方法来清空对象,使其不监测任何资源

    3. 解决了share_ptr 的哪些问题

    1. 返回管理this的shared_ptr
      1. 上面介绍的shared_from_this()其实就是通过weak_ptr返回的this指针
    1. 解决循环引用问题

    Demo

    struct A;
    struct B;
    
    struct A {
        std::shared_ptr bptr;
        ~A() {
            cout << "A delete" << endl;
        }
        void Print() {
            cout << "A" << endl;
        }
    };
    
    struct B {
        std::weak_ptr aptr; // 这里改成weak_ptr
        ~B() {
            cout << "B delete" << endl;
        }
        void PrintA() {
            if (!aptr.expired()) { // 监视shared_ptr的生命周期
                auto ptr = aptr.lock();
                ptr->Print();
            }
        }
    };
    
    int main() {
        auto aaptr = std::make_shared();
        auto bbptr = std::make_shared();
        aaptr->bptr = bbptr;
        bbptr->aptr = aaptr;
        bbptr->PrintA();
        return 0;
    }
    输出:
    A
    A delete
    B delete

    shared_ptr 源码分析

    gcc版本源码分析图

    分析

    1. shared_ptr类几乎什么都没有做,它是继承了__shared_ptr
    2. __shared_ptr内部有一个类型为__shared_count类型的成员_M_refcount
    3. __shared_count内部有类型为_Sp_counted_base*的_M_pi的成员, _Sp_counted_base才是整个shared_ptr功能的核心,通过_Sp_counted_base控制引用计数来管理托管的内存
    4. Sp_counted_base内部不持有托管内存的指针,__shared_count内部的成员其实是一个继承自_Sp_counted_base的_Sp_counted_ptr类型
    5. _Sp_counted_ptr类型内部持有托管内存的指针_M_ptr
    6. , _M_use_count表示托管对象的引用计数,控制托管对象什么时候析构和释放
    7. M_weak_count表示管理对象的引用计数,管理对象也是一个内存指针,这块指针是初始化第一个shared_ptr时new出来的,到最后也需要delete

    _M_use_count是如何加减的

    template 
    __shared_ptr(const __shared_ptr<_Yp, _Lp>& __r,
                    element_type* __p) noexcept
        : _M_ptr(__p), _M_refcount(__r._M_refcount)  // never throws
    {
    }
    
    __shared_count(const __shared_count& __r) noexcept : _M_pi(__r._M_pi)
    {
        if (_M_pi != 0) _M_pi->_M_add_ref_copy();
    }
    
    template <>
    inline void _Sp_counted_base<_S_single>::_M_add_ref_copy()
    {
        ++_M_use_count;
    }
    
    template 
    _Assignable&> operator=(
        const shared_ptr<_Yp>& __r) noexcept
    {
        this->__shared_ptr<_Tp>::operator=(__r);
        return *this;
    }
    
    template 
    _Assignable<_Yp> operator=(const __shared_ptr<_Yp, _Lp>& __r) noexcept
    {
        _M_ptr = __r._M_ptr;
        _M_refcount = __r._M_refcount;  // __shared_count::op= doesn't throw
        return *this;
    }
    
    __shared_count& operator=(const __shared_count& __r) noexcept
    {
        _Sp_counted_base<_Lp>* __tmp = __r._M_pi;
        if (__tmp != _M_pi) {
            if (__tmp != 0) __tmp->_M_add_ref_copy();
            if (_M_pi != 0) _M_pi->_M_release();
            _M_pi = __tmp;
        }
        return *this;
    }
    
    ~__shared_count() noexcept
    {
        if (_M_pi != nullptr) _M_pi->_M_release();
    }
    
    template <>
    inline void _Sp_counted_base<_S_single>::_M_release() noexcept
    {
        if (--_M_use_count == 0) {
            _M_dispose();
            if (--_M_weak_count == 0) _M_destroy();
        }
    }
    
    virtual void _M_dispose() noexcept { delete _M_ptr; }

     

  • 相关阅读:
    (附源码)springboot通用办事流程管理软件 毕业设计 211819
    513.找树左下角的值
    函数,递归以及dom简单操作
    jwt对token的生成以及验证机制
    【QT开发(10)】QT 进程
    程序员副业之无货源闲鱼
    探花交友_第4章_圈子功能实现
    使用 Visual Studio Code 配置 C/C++ 开发环境
    uniApp初体验-从零开始注意事项
    【高项笔记】No.2 网络和网络协议
  • 原文地址:https://blog.csdn.net/qq_32378713/article/details/126260501