千家信息网

C++11中智能指针shared_ptr、weak_ptr的示例分析

发表于:2024-12-12 作者:千家信息网编辑
千家信息网最后更新 2024年12月12日,这篇文章给大家分享的是有关C++11中智能指针shared_ptr、weak_ptr的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1、前言本文仅对C++智能指针s
千家信息网最后更新 2024年12月12日C++11中智能指针shared_ptr、weak_ptr的示例分析

这篇文章给大家分享的是有关C++11中智能指针shared_ptr、weak_ptr的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

1、前言

本文仅对C++智能指针shared_ptr、weak_ptr源码进行解析,需要读者有一定的C++基础并且对智能指针有所了解,本文并不对智能指针的使用方法、使用场景、效率等方面进行阐述分析,这些知识需自行查阅相关书籍去了解

2、源码准备

本文是基于gcc-4.9.0的源代码进行分析,shared_ptr和weak_ptr是C++11才加入标准的,所以低版本的gcc源码是没有shared_ptr和weak_ptr的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的,下面给出源码下载地址
http://ftp.gnu.org/gnu/gcc

3、智能指针概念

智能指针(Smart pointers)是存储"指向动态分配(在堆上)的对象的指针"的对象。也就是说,智能指针其实是个对象。不过它的行为很像C++的内建指针,只是它们可以在适当的时候自动删除它们所指向的对象。智能指针在面对异常时有非常显著的作用,它们可以确保动态分配对象的完全析构。它们还可以用于跟踪多主人共享的动态分配对象。在概念上,智能指针可以看作拥有它所指向的对象,并因此在对象不再需要时负责将它删除。

4、源码解析

4.1、shared_ptr解析

4.1.1、shared_ptr

shared_ptr位于libstdc++-v3\include\bits\shared_ptr.h

templateclass shared_ptr : public __shared_ptr<_Tp>{public:...        // 构造函数        template        explicit shared_ptr(_Tp1* __p)                :__shared_ptr<_Tp>(__p)        {        }...};

由于源代码过长,这里就只贴出其中一部分进行分析:

  1. 该类没有类成员

  2. 该类继承于__shared_ptr,构造函数也只是调用了__shared_ptr的构造函数而已,将接管的普通指针传递给__shared_ptr

  3. 该类没有重载*->运算符,从这点看shared_ptr似乎无法实现普通指针的功能,推测这两个运算符的重载是在父类__shared_ptr实现的

  4. 该类没有析构函数,从智能指针最终会自动释放内存的特性来看,释放工作肯定不是在该类进行了,接下来分析父类__shared_ptr的实现

4.1.2、__shared_ptr

__shared_ptr位于libstdc++-v3\include\bits\shared_ptr_base.h

templateclass __shared_ptr{public:    typedef _Tp element_type;...        // 构造函数        template        explicit __shared_ptr(_Tp1* __p)                :_M_ptr(__p), _M_refcount(__p)        {                __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)                static_assert( !is_void<_Tp>::value, "incomplete type" );                static_assert( sizeof(_Tp1) > 0, "incomplete type" );                __enable_shared_from_this_helper(_M_refcount, __p, __p);        }        // 析构函数        ~__shared_ptr() = default;        typename std::add_lvalue_reference<_Tp>::type        operator*() const noexcept        {                _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);                return *_M_ptr;        }        _Tp*        operator->() const noexcept        {                _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);                return _M_ptr;        }...private:    _Tp* _M_ptr;    // Contained pointer.    __shared_count<_Lp> _M_refcount;    // Reference counter.};

同样的,源代码比较长且不是分析的重点,所以只贴出一部分进行分析:

可以看到里面有两个类成员:_M_ptr(由智能指针接管的普通指针)、_M_refcount(引用计数器,类型为__shared_count)

  1. 从构造函数看,_M_ptr获得了接管的普通指针的值,而_M_refcount的构造也同样需要这个值

  2. 重载了*->运算符,由shared_ptr继承使用,使得智能指针最终能拥有和普通指针一样行为,尽管智能指针本质上是一个对象

  3. 从析构函数来看,里面啥也没做,说明接管的普通指针也不是在这里释放的,所以有可能是由_M_refcount来完成释放内存这个工作,下面分析__shared_count的实现

4.1.3、__shared_count

__shared_count位于libstdc++-v3\include\bits\shared_ptr_base.h

template<_Lock_policy _Lp>class __shared_count{public:    constexpr __shared_count() noexcept : _M_pi(0)    {    }    template    explicit __shared_count(_Ptr __p) : _M_pi(0)    {        __try        {            _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);        }        __catch(...)        {            delete __p;            __throw_exception_again;        }    }    template    __shared_count(_Ptr __p, _Deleter __d)        :__shared_count(__p, std::move(__d), allocator())    {    }    template    __shared_count(_Ptr __p, _Deleter __d, _Alloc __a)        :_M_pi(0)    {        typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type;        typedef typename allocator_traits<_Alloc>::template rebind_traits<_Sp_cd_type> _Alloc_traits;        typename _Alloc_traits::allocator_type __a2(__a);        _Sp_cd_type* __mem = 0;        __try        {            __mem = _Alloc_traits::allocate(__a2, 1);            _Alloc_traits::construct(__a2, __mem, __p, std::move(__d), std::move(__a));            _M_pi = __mem;        }        __catch(...)        {            __d(__p); // Call _Deleter on __p.            if (__mem)                _Alloc_traits::deallocate(__a2, __mem, 1);            __throw_exception_again;        }    }    template    __shared_count(_Sp_make_shared_tag, _Tp*, const _Alloc& __a, _Args&&... __args)        :_M_pi(0)    {        typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type;        typedef typename allocator_traits<_Alloc>::template rebind_traits<_Sp_cp_type> _Alloc_traits;        typename _Alloc_traits::allocator_type __a2(__a);        _Sp_cp_type* __mem = _Alloc_traits::allocate(__a2, 1);        __try        {            _Alloc_traits::construct(__a2, __mem, std::move(__a),            std::forward<_Args>(__args)...);            _M_pi = __mem;        }        __catch(...)        {            _Alloc_traits::deallocate(__a2, __mem, 1);            __throw_exception_again;        }    }    template    explicit __shared_count(std::unique_ptr<_Tp, _Del>&& __r)        :_M_pi(0)    {        using _Ptr = typename unique_ptr<_Tp, _Del>::pointer;        using _Del2 = typename conditional::value, reference_wrapper::type>, _Del>::type;        using _Sp_cd_type = _Sp_counted_deleter<_Ptr, _Del2, allocator, _Lp>;        using _Alloc = allocator<_Sp_cd_type>;        using _Alloc_traits = allocator_traits<_Alloc>;        _Alloc __a;        _Sp_cd_type* __mem = _Alloc_traits::allocate(__a, 1);        _Alloc_traits::construct(__a, __mem, __r.release(), __r.get_deleter());  // non-throwing        _M_pi = __mem;    }    explicit __shared_count(const __weak_count<_Lp>& __r);    explicit __shared_count(const __weak_count<_Lp>& __r, std::nothrow_t);    ~__shared_count() noexcept    {        if (_M_pi != nullptr)            _M_pi->_M_release();    }    __shared_count(const __shared_count& __r) noexcept        :_M_pi(__r._M_pi)    {        if (_M_pi != 0)            _M_pi->_M_add_ref_copy();    }    __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;    }    void _M_swap(__shared_count& __r) noexcept    {        _Sp_counted_base<_Lp>* __tmp = __r._M_pi;        __r._M_pi = _M_pi;        _M_pi = __tmp;    }    long _M_get_use_count() const noexcept    { return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; }    bool _M_unique() const noexcept    { return this->_M_get_use_count() == 1; }    void* _M_get_deleter(const std::type_info& __ti) const noexcept    { return _M_pi ? _M_pi->_M_get_deleter(__ti) : nullptr; }    bool _M_less(const __shared_count& __rhs) const noexcept    { return std::less<_Sp_counted_base<_Lp>*>()(this->_M_pi, __rhs._M_pi); }    bool _M_less(const __weak_count<_Lp>& __rhs) const noexcept    { return std::less<_Sp_counted_base<_Lp>*>()(this->_M_pi, __rhs._M_pi); }    friend inline bool operator==(const __shared_count& __a, const __shared_count& __b) noexcept    { return __a._M_pi == __b._M_pi; }private:    friend class __weak_count<_Lp>;    _Sp_counted_base<_Lp>* _M_pi;}

从源代码可以获得以下几点信息:

有一个类成员:_M_pi(计数器,类型为_Sp_counted_base)

  1. 只有构造函数为_M_pi分配了内存,并且该类并没有直接持有从前面一直传递过来的那个普通指针,而是继续将其传递给_M_pi,所以内存的释放也不是直接在该类进行的。

  2. 拷贝构造函数没有分配内容,而是把拷贝对象的_M_pi直接拿过来了,有点类似于浅拷贝的意思,然后调用了_M_pi_M_add_ref_copy方法(后面会讲),增加了一次引用计数。赋值函数也是同样的道理,但是由于赋值函数的特殊性(当赋值对象原先就存在时调用赋值函数,否则调用拷贝构造函数),要先调用_M_pi_M_release方法(后面会讲)将自己持有的内存释放掉,其余操作和拷贝构造函数是一样的

  3. 从析构函数中可以看到,里面并没有直接释放掉为_M_pi分配的内存,而是调用了_M_pi_M_release方法,可以大概猜测是通过_M_release方法释放了_M_pi的内存(delete this指针,后面会讲)

  4. 由于__shared_count里面的方法都是借助_M_pi实现的,并且到这里都还没有见到释放那个普通指针的代码,所以还是得继续看_M_pi究竟做了什么工作,接下来继续看_Sp_counted_base的实现

4.1.4、_Sp_counted_base

_Sp_counted_base位于libstdc++-v3\include\bits\shared_ptr_base.h

template<_Lock_policy _Lp = __default_lock_policy>class _Sp_counted_base : public _Mutex_base<_Lp>{public:    _Sp_counted_base() noexcept        : _M_use_count(1), _M_weak_count(1)    {    }      virtual ~_Sp_counted_base() noexcept    {    }    virtual void _M_dispose() noexcept = 0;      virtual void _M_destroy() noexcept    { delete this; }    virtual void* _M_get_deleter(const std::type_info&) noexcept = 0;    void _M_add_ref_copy()    { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }    void _M_add_ref_lock();    bool _M_add_ref_lock_nothrow();    void _M_release() noexcept    {        _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);        if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1)        {            _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);            _M_dispose();            if (_Mutex_base<_Lp>::_S_need_barriers)            {                _GLIBCXX_READ_MEM_BARRIER;                _GLIBCXX_WRITE_MEM_BARRIER;            }            _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);            if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1)            {                _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);                _M_destroy();            }        }    }    void _M_weak_add_ref() noexcept    { __gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); }    void _M_weak_release() noexcept    {        _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);        if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1)        {            _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);            if (_Mutex_base<_Lp>::_S_need_barriers)            {                _GLIBCXX_READ_MEM_BARRIER;                _GLIBCXX_WRITE_MEM_BARRIER;            }            _M_destroy();        }    }    long _M_get_use_count() const noexcept    {        return __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED);    }private:    _Sp_counted_base(_Sp_counted_base const&) = delete;    _Sp_counted_base& operator=(_Sp_counted_base const&) = delete;    _Atomic_word  _M_use_count;     // #shared    _Atomic_word  _M_weak_count;    // #weak + (#shared != 0)};

从源代码可以获得以下几点信息:

  1. 有两个类成员:_M_use_count(引用计数)、_M_weak_count(弱引用计数),对这两个数的操作需要具有原子性

  2. _M_release方法是该类的关键,可以看到先将_M_use_count自减1,然后判断自减前_M_use_count的值是否为1(无其他人引用),如果为1,则调用_M_dispose方法(虚函数,由派生类实现,估计是释放前面一直说的那个由智能指针接管的普通指针)。接下来将_M_weak_count自减1,然后判断自减前_M_weak_count的值是否为1(无其他人引用),如果为1,则调用_M_destroy方法,而_M_destroy方法里面释放了this指针,这点和前面的猜测一致

  3. _M_release可以看出,智能指针所接管的指针的释放内存工作只和_M_use_count有关,当_M_use_count减完时就会将其释放了,而_M_weak_count也是有作用的,他负责释放_Sp_counted_base本身,这也就是为什么weak_ptr可以保证智能指针这个对象有效,但不保证智能指针所引用的指针有效的原因了(这点和shared_ptr、weak_ptr的定义是完全一致的)

  4. 其他的方法就很简单了,比如_M_add_ref_copy方法将引用计数_M_use_count加一,_M_weak_add_ref方法将弱引用计数_M_weak_count加一,这个自增过程是具有原子性的,这里就不赘述了,大家可以自行看一下具体实现

4.1.5、_Sp_counted_ptr

_Sp_counted_ptr位于libstdc++-v3\include\bits\shared_ptr_base.h

templateclass _Sp_counted_ptr final : public _Sp_counted_base<_Lp>{public:    explicit _Sp_counted_ptr(_Ptr __p) noexcept : _M_ptr(__p)    {    }    virtual void _M_dispose() noexcept    { delete _M_ptr; }    virtual void _M_destroy() noexcept    { delete this; }    virtual void* _M_get_deleter(const std::type_info&) noexcept    { return nullptr; }    _Sp_counted_ptr(const _Sp_counted_ptr&) = delete;    _Sp_counted_ptr& operator=(const _Sp_counted_ptr&) = delete;private:    _Ptr _M_ptr;};
  1. 从源代码中可以看到_Sp_counted_ptr_Sp_counted_base的派生类,并且__shared_count在初始化_M_pi时用的也是_Sp_counted_ptr

  2. 接着看_M_dispose方法的实现,里面确实删除了一开始shared_ptr接管的指针,_M_destroy方法用于释放自己的内存(由__shared_count调用),和前面猜想一致

4.1.6、shared_ptr总结

看完前面分析的内容再回过头来看,_Sp_counted_base_M_add_ref_copy方法是整个流程的关键,它实现了引用计数器的增加,那么在何时调用它就是关键了。通过在代码中检索,可以查到__shared_count的赋值构造函数和拷贝构造函数调用了它(其实也只有可能是这里啦,因为只有它的类成员有_Sp_counted_base),这样整个流程也就解释通了:

  1. __shared_count的成员_M_pi只会初始化一次(构造函数中分配内存初始化的)

  2. 后面调用拷贝构造时(这个行为由__shared_ptr触发,__shared_ptr的拷贝构造函数和赋值函数都会调用__shared_count的拷贝构造函数),__shared_count只是简单复制了_M_pi而已,并没有重新分配内存,然后再调用_M_add_ref_copy增加一次引用计数,这样就实现了shared_ptr每多一份拷贝就增加一次引用计数的特性了

  3. 每一个__shared_count被析构都会使引用计数减一,减完就将智能指针持有的资源释放,这个前面已经分析过了,这里就不赘述了

4.2、weak_ptr解析

4.2.1、weak_ptr

weak_ptr位于libstdc++-v3\include\bits\shared_ptr.h

templateclass weak_ptr : public __weak_ptr<_Tp>{public:        constexpr weak_ptr() noexcept                :__weak_ptr<_Tp>()        {        }        template::value>::type>        weak_ptr(const weak_ptr<_Tp1>& __r) noexcept                :__weak_ptr<_Tp>(__r)        {        }        template::value>::type>        weak_ptr(const shared_ptr<_Tp1>& __r) noexcept                :__weak_ptr<_Tp>(__r)        {        }        template        weak_ptr& operator=(const weak_ptr<_Tp1>& __r) noexcept        {                this->__weak_ptr<_Tp>::operator=(__r);                return *this;        }        template        weak_ptr& operator=(const shared_ptr<_Tp1>& __r) noexcept        {                this->__weak_ptr<_Tp>::operator=(__r);                return *this;        }        shared_ptr<_Tp>        lock() const noexcept        {                return shared_ptr<_Tp>(*this, std::nothrow);        }}

从源代码中可以看出以下几点:

  1. 该类没有类成员

  2. 从构造函数的参数来看(无参构造函数除外),只能使用shared_ptrweak_ptr来构造一个weak_ptr对象,包括赋值函数也是这样的,这就和shared_ptr有很大区别了,从4.1.1小节可以看到shared_ptr是可以使用普通指针来构造的

  3. 可以调用lock方法来获得一个shared_ptrlock方法的实现后面再讲

该类没有重载*->运算符,接下来分析其父类__weak_ptr的实现

4.2.2、__weak_ptr

__weak_ptr位于libstdc++-v3\include\bits\shared_ptr_base.h

templateclass __weak_ptr{public:        typedef _Tp element_type;        constexpr __weak_ptr() noexcept                :_M_ptr(0)                ,_M_refcount()        {        }        __weak_ptr(const __weak_ptr&) noexcept = default;        __weak_ptr& operator=(const __weak_ptr&) noexcept = default;        ~__weak_ptr() = default;                template::value>::type>        __weak_ptr(const __weak_ptr<_Tp1, _Lp>& __r) noexcept                :_M_refcount(__r._M_refcount)        { _M_ptr = __r.lock().get(); }        template::value>::type>        __weak_ptr(const __shared_ptr<_Tp1, _Lp>& __r) noexcept                :_M_ptr(__r._M_ptr)                ,_M_refcount(__r._M_refcount)        {        }        template        __weak_ptr& operator=(const __weak_ptr<_Tp1, _Lp>& __r) noexcept        {                _M_ptr = __r.lock().get();                _M_refcount = __r._M_refcount;                return *this;        }        template        __weak_ptr& operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept        {                _M_ptr = __r._M_ptr;                _M_refcount = __r._M_refcount;                return *this;        }        __shared_ptr<_Tp, _Lp> lock() const noexcept        { return __shared_ptr(*this, std::nothrow); }        long use_count() const noexcept        { return _M_refcount._M_get_use_count(); }        bool expired() const noexcept        { return _M_refcount._M_get_use_count() == 0; }        template        bool owner_before(const __shared_ptr<_Tp1, _Lp>& __rhs) const        { return _M_refcount._M_less(__rhs._M_refcount); }        template        bool owner_before(const __weak_ptr<_Tp1, _Lp>& __rhs) const        { return _M_refcount._M_less(__rhs._M_refcount); }        void reset() noexcept        { __weak_ptr().swap(*this); }        void swap(__weak_ptr& __s) noexcept        {                std::swap(_M_ptr, __s._M_ptr);                _M_refcount._M_swap(__s._M_refcount);        }private:        // Used by __enable_shared_from_this.        void _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept        {                _M_ptr = __ptr;                _M_refcount = __refcount;        }        template friend class __shared_ptr;        template friend class __weak_ptr;        friend class __enable_shared_from_this<_Tp, _Lp>;        friend class enable_shared_from_this<_Tp>;        _Tp* _M_ptr;         // Contained pointer.        __weak_count<_Lp> _M_refcount;    // Reference counter.}

从源代码中可以看出以下几点信息:

  1. 有两个类成员:_M_ptr(由智能指针接管的普通指针)、_M_refcount(弱引用计数器,类型为__weak_count)

  2. 从构造函数看,_M_ptr获得了接管的普通指针的值,而_M_refcount的构造并不需要这个值了(这点和__shared_ptr不一样了),_M_refcount只能借助其他__shared_ptr_M_refcount或者__weak_ptr_M_refcount来进行构造(注意这两个的_M_refcount类型不同,说明__weak_count支持多种类型进行构造)

  3. 拷贝构造函数和赋值函数的实现同上

  4. 该类依然没有重载*->运算符,由于接下去已无继承关系,所以weak_ptr不具备普通指针的特性,无法直接使用资源,这点符合weak_ptr的定义

  5. 既然weak_ptr无法直接使用资源,那他设计_M_ptr这个成员的意图在哪里呢?答案就是lock方法将weak_ptr转换为shared_ptr时是需要将这个指针传递过去的,不然连接管的指针都没了转换的意义也就没了

  6. 析构函数啥也没做,因为weak_ptr不持有资源,不对资源的释放产生影响,接下来对__weak_count进行分析

4.2.3、__weak_count

__weak_count的实现位于libstdc++-v3\include\bits\shared_ptr_base.h

template<_Lock_policy _Lp>class __weak_count{public:        constexpr __weak_count() noexcept : _M_pi(0)        {        }        __weak_count(const __shared_count<_Lp>& __r) noexcept                :_M_pi(__r._M_pi)        {                if (_M_pi != 0)                        _M_pi->_M_weak_add_ref();        }        __weak_count(const __weak_count<_Lp>& __r) noexcept                :_M_pi(__r._M_pi)        {                if (_M_pi != 0)                        _M_pi->_M_weak_add_ref();        }        ~__weak_count() noexcept        {                if (_M_pi != 0)                        _M_pi->_M_weak_release();        }        __weak_count<_Lp>&        operator=(const __shared_count<_Lp>& __r) noexcept        {                _Sp_counted_base<_Lp>* __tmp = __r._M_pi;                if (__tmp != 0)                        __tmp->_M_weak_add_ref();                if (_M_pi != 0)                        _M_pi->_M_weak_release();                _M_pi = __tmp;                return *this;        }        __weak_count<_Lp>& operator=(const __weak_count<_Lp>& __r) noexcept        {                _Sp_counted_base<_Lp>* __tmp = __r._M_pi;                if (__tmp != 0)                        __tmp->_M_weak_add_ref();                if (_M_pi != 0)                        _M_pi->_M_weak_release();                _M_pi = __tmp;                return *this;        }        void _M_swap(__weak_count<_Lp>& __r) noexcept        {                _Sp_counted_base<_Lp>* __tmp = __r._M_pi;                __r._M_pi = _M_pi;                _M_pi = __tmp;        }        long _M_get_use_count() const noexcept        { return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; }        bool _M_less(const __weak_count& __rhs) const noexcept        { return std::less<_Sp_counted_base<_Lp>*>()(this->_M_pi, __rhs._M_pi); }        bool _M_less(const __shared_count<_Lp>& __rhs) const noexcept        { return std::less<_Sp_counted_base<_Lp>*>()(this->_M_pi, __rhs._M_pi); }        friend inline bool operator==(const __weak_count& __a, const __weak_count& __b) noexcept        { return __a._M_pi == __b._M_pi; }private:        friend class __shared_count<_Lp>;        _Sp_counted_base<_Lp>*  _M_pi;}

从源代码可以获得以下几点信息:

  1. 有一个类成员:_M_pi(计数器,类型为_Sp_counted_base)

  2. 仔细一看__shared_count里也持有这个成员,类型一模一样,这样也就解释得通为什么__shared_count__weak_count可以互相转换了,转换的方式很简单:

__shared_count转换为__weak_count的过程为:
拷贝_M_pi,然后调用_M_weak_add_ref方法增加一次弱引用计数__weak_count转换为__shared_count的过程为:
拷贝_M_pi,然后调用_M_add_ref_copy方法增加一次引用计数
  1. 构造函数、拷贝构造函数、赋值函数均不为_M_pi分配了内存,这点也可以看出weak_ptr确实是shared_ptr的附属品而已,自己不持有资源不控制资源

  2. 析构函数中调用了_M_pi_M_weak_release方法,释放了_M_pi的内存(条件满足的情况下才会释放)

  3. 接下来的内容和3.1.4小节还有3.1.5小节的内容是一样的,这里就不赘述

4.2.4、回过头看weak_ptr中lock方法的实现

weak_ptrlock方法调用了shared_ptr的构造函数如下:

shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t)    :__shared_ptr<_Tp>(__r, std::nothrow){}

从上面的代码可以看出调用了__shared_ptr的构造函数,代码如下:

__shared_ptr(const __weak_ptr<_Tp, _Lp>& __r, std::nothrow_t)    :_M_refcount(__r._M_refcount, std::nothrow){    _M_ptr = _M_refcount._M_get_use_count() ? __r._M_ptr : nullptr;}

可以看到此时先是使用了__weak_ptr_M_refcount成员(类型为__weak_count)来构造__shared_ptr_M_refcount成员(类型为__shared_count),然后再判断引用计数器是否为0,为零的话就将__shared_ptr_M_ptr成员置为nullptr,即lock函数执行失败;不为零的话就会正常构建一个shared_ptr了。

上面讲的构造_M_refcount的方法如下所示:

template<_Lock_policy _Lp>inline __shared_count<_Lp>::__shared_count(const __weak_count<_Lp>& __r, std::nothrow_t)    :_M_pi(__r._M_pi){    if (_M_pi != nullptr)        if (!_M_pi->_M_add_ref_lock_nothrow())            _M_pi = nullptr;}template<>inline bool _Sp_counted_base<_S_single>::_M_add_ref_lock_nothrow(){    if (_M_use_count == 0)        return false;    ++_M_use_count;    return true;}

从上面的代码中我们可以看到,首先__shared_count使用__weak_count_M_pi来构建自己的_M_pi,从前面的分析我们可以知道,在所有的shared_ptrweak_ptr消亡之前,_M_pi的内存是不会被释放的,所以这里就算之前的shared_ptr已经全部消亡(即资源已释放),_M_pi还是有效的(因为weak_ptr还没有消亡)。而通过判断_M_add_ref_lock_nothrow的返回值来确定是否要将_M_pi置为nullptr,可以看到判断的条件为_M_use_count是否为0(即判断资源是否被释放了)。

接下来再看一下__shared_count_M_get_use_count方法,代码如下:

long _M_get_use_count() const noexcept{ return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0; }

代码比较简单,意思就是如果此时资源已经被释放了(对应_M_pi值为nullptr),则会返回0,再回到上面第2点讲的那里,_M_ptr将被设置为nullptr,即资源无效,lock函数执行失败。
至此weak_ptrlock方法的实现原理就全部讲解完毕。

4.3、enable_shared_from_this解析

4.3.1、从一个典型的例子来认识智能指针的不足之处

有时候我们需要在一个被shared_ptr管理的对象的内部获取自己的shared_ptr,比如下面这个的例子:

class Ptr{public:        void fun()        {                std::shared_ptr p(this);                std::cout << sp->use_count() << std::endl;        }};std::shared_ptr p= std::make_shared();p->fun(); //输出为1

从上面这个简单的例子可以看到,fun输出的居然是1而不是2,这是为什么?倒回去4.1.2小节可以看到,当使用普通指针(上面的那个this)去构造shared_ptr时,构造出来的shared_ptr一定是独立的,不与其他人共享的。这样就会出现一个非常严重的问题,那就是析构时会导致对象被重复释放, 从而引发错误

4.3.2、改进方法

现在明确一下我们的需求:在一个对象内部构造该对象的shared_ptr 时,即使该对象已经被shared_ptr管理着,也不会造成对象被两个独立的智能指针管理。这就要求我们在对象内构造对象的智能指针时,必须能识别有对象是否已经由其他智能指针管理,智能指针的数量,并且我们创建智能指针后也能让之前的智能指针感知到。当然标准已经也给出了解决了这个问题办法,那就是使用接下来所提到的enable_shared_from_this

4.3.3、enable_shared_from_this解析

enable_shared_from_this的实现位于libstdc++-v3\include\bits\shared_ptr.h

templateclass enable_shared_from_this{protected:        constexpr enable_shared_from_this() noexcept { }        enable_shared_from_this(const enable_shared_from_this&) noexcept { }        enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept        { return *this; }        ~enable_shared_from_this() { }public:        shared_ptr<_Tp> shared_from_this()        { return shared_ptr<_Tp>(this->_M_weak_this); }        shared_ptr shared_from_this() const        { return shared_ptr(this->_M_weak_this); }private:        template        void _M_weak_assign(_Tp1* __p, const __shared_count<>& __n) const noexcept        { _M_weak_this._M_assign(__p, __n); }        template        friend void __enable_shared_from_this_helper(const __shared_count<>& __pn, const enable_shared_from_this* __pe, const _Tp1* __px) noexcept        {                if (__pe != 0)                        __pe->_M_weak_assign(const_cast<_Tp1*>(__px), __pn);        }        mutable weak_ptr<_Tp>  _M_weak_this;};

从源代码可以获得以下几点信息:

  1. 有一个类成员:_M_weak_this

  2. 该类需要被继承,被需要用智能指针管理的对象继承

  3. 我们平时就是使用该类的shared_from_this方法的,可以看到其实现就是利用_M_weak_this构造一个shared_ptr对象而已

  4. 该类并没有直接初始化_M_weak_this,而是提供了_M_weak_assign方法来构造_M_weak_this,其实现比较简单,就是调用了weak_ptr_M_assign方法

  5. 那么问题来了,_M_weak_assign方法由谁调用呢?从后面我们可以知道是由一个全局函数__enable_shared_from_this_helper调用的,该函数有一种重载形式是enable_shared_from_this的友元函数,从上面的代码中就可以看到了,那唯一一个友元函数就是__enable_shared_from_this_helper,里面调用了enable_shared_from_this_M_weak_assign方法。

  6. __enable_shared_from_this_helper函数要在哪个时间点使用才能达到预期的效果呢?答案当然是在__shared_ptr的构造函数中调用。下面列出了__shared_ptr部分构造函数,可以看到确实调用了__enable_shared_from_this_helper,证实了前面的猜想

templateclass __shared_ptr{public:...        template        explicit __shared_ptr(_Tp1* __p)                :_M_ptr(__p)                ,_M_refcount(__p)        {                __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)                static_assert( !is_void<_Tp>::value, "incomplete type" );                static_assert( sizeof(_Tp1) > 0, "incomplete type" );                __enable_shared_from_this_helper(_M_refcount, __p, __p);        }        template        __shared_ptr(_Tp1* __p, _Deleter __d)                :_M_ptr(__p)                ,_M_refcount(__p, __d)        {                __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)                __enable_shared_from_this_helper(_M_refcount, __p, __p);        }        template        __shared_ptr(_Tp1* __p, _Deleter __d, _Alloc __a)                :_M_ptr(__p)                ,_M_refcount(__p, __d, std::move(__a))        {                __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)                __enable_shared_from_this_helper(_M_refcount, __p, __p);        }...};
4.3.4、__enable_shared_from_this_helper解析

__enable_shared_from_this_helper的实现位于libstdc++-v3\include\bits\shared_ptr_base.h

// Friend of enable_shared_from_this.templatevoid __enable_shared_from_this_helper(const __shared_count<>&, const enable_shared_from_this<_Tp1>*, const _Tp2*) noexcept;template<_Lock_policy _Lp>inline void __enable_shared_from_this_helper(const __shared_count<_Lp>&, ...) noexcept{ }

这里有必要再看一下__enable_shared_from_this_helper函数的实现,有两种形式,第一种就是上面提到过的那个enable_shared_from_this的友元函数,而第二种重载形式里面啥都没有干。为什么需要重载这两个函数呢?答案很简单,当我们一个类继承了enable_shared_from_this之后,这个类肯定可以转换为enable_shared_from_this类型了,此时在__shared_ptr中调用的__enable_shared_from_this_helper就是上面第一种情况了,这种情况下就可以使用shared_from_this函数了;反之,当类没有继承enable_shared_from_this时,就是调用第二中形式的__enable_shared_from_this_helper,此时也就不能使用shared_from_this函数了。
至此,为什么在使用shared_from_this前,对应的类需要继承enable_shared_from_this的原因也就全部揭晓了。

感谢各位的阅读!关于"C++11中智能指针shared_ptr、weak_ptr的示例分析"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

指针 函数 方法 智能 对象 成员 分析 普通 内存 拷贝 就是 资源 源代码 类型 两个 代码 分配 接下来 内容 源码 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 数据库建在西部的主要原因是什么 服务器内存报错提示 上海卓妍网络技术有限公司 黑客攻击需要一个服务器吗 游戏软件开发公司框架 网络安全去中科院与华科读研 江西智能化软件开发市场报价 软件开发技术知识点归纳总结 关于网络安全中不良信息的文章 asp 数据库 增删改查 mysql数据库不可写 服务器主板启动方式 计算机网络技术工程系统 魔兽怀旧服玩家服务器在哪 数据库链表查询时间过长 重庆华为服务器维修维保云主机 网络安全为什么选择武汉 软件开发简介ppt 兽系统的数据库有什么用 软件开发和测试到底哪一个更好 中国电子网络安全创新发展 电子商务数据库安全设置视频教学 西安wms软件开发 联想服务器默认bmc 西安欧维软件开发有限公司 5g网络技术安装 电脑找不到服务器配置 浙江综合软件开发商家 sccm管理服务器 云服务器投资
0