精华内容
下载资源
问答
  • C++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智能指针引用。该引用计数的内存在堆上分配。当新增一个时引用计数加1,当过期时引用计数减一。只有引用计数为0时,智能...
  • 主要介绍了 C++11智能指针之weak_ptr详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • 主要介绍了C++11新特性之智能指针,包括shared_ptr, unique_ptrweak_ptr的基本使用,感兴趣的小伙伴们可以参考一下
  • C++ 智能指针(shared_ptr/weak_ptr)源码 源码位置:gcc-6.1.0\gcc-6.1.0\libstdc++-v3\include\tr1 这里只单列shared_ptr.h文件用于分析
  • 关于C++智能指针 weak_ptr (弱引用 智能指针) 和 shared_ptr (强引用 智能指针)的源文件。 资源分不能设为0了。。
  • shared_ptrweak_ptr

    2021-06-12 12:25:41
    shared_ptr 参考自https://www.jianshu.com/p/b6ac02d406a0 shared_ptr类几乎什么都没有做,它是继承了__shared_ptr, __shared_ptr内部有一个类型为__shared_count类型的成员_M_refcount, __shared_count内部有类型...

    shared_ptr

    参考自https://www.jianshu.com/p/b6ac02d406a0

    shared_ptr类几乎什么都没有做,它是继承了__shared_ptr, __shared_ptr内部有一个类型为__shared_count类型的成员_M_refcount, __shared_count内部有类型为_Sp_counted_base*的_M_pi的成员, _Sp_counted_base才是整个shared_ptr功能的核心,通过_Sp_counted_base控制引用计数来管理托管的内存,由图可见_Sp_counted_base内部不持有托管内存的指针,这里__shared_count内部的成员其实是一个继承自_Sp_counted_base的_Sp_counted_ptr类型,_Sp_counted_ptr类型内部持有托管内存的指针_M_ptr, _M_pi是一个_Sp_counted_base基类对象指针,指向_Sp_counted_ptr子类对象内存,这样_M_pi内部就既可以控制引用计数,又可以在最后释放托管内存。

    这里称_M_pi为管理对象,它内部的_M_ptr为托管对象,管理同一块托管对象的多个shared_ptr内部共用一个管理对象(_M_pi), 这里的多个shared_ptr可能是通过第一个shared_ptr拷贝或者移动而来, 管理对象内部有两个成员变量_M_use_count和_M_weak_count, _M_use_count表示托管对象的引用计数,控制托管对象什么时候析构和释放,大概就是有N个shared_ptr的拷贝那引用计数就是N,当引用计数为0时调用托管对象的析构函数且释放内存。_M_weak_count表示管理对象的引用计数,管理对象也是一个内存指针,这块指针是初始化第一个shared_ptr时new出来的,到最后也需要delete,所以使用_M_weak_count来控制管理对象什么时候析构,我们平时用到的weak_ptr内部其实持有的就是这个管理对象的指针,当weak_ptr拷贝时,管理对象的引用计数_M_weak_count就会增加,当_M_weak_count为0时,管理对象_M_pi就会析构且释放内存。

    class shared_ptr

    template <typename _Tp> class shared_ptr : public __shared_ptr<_Tp>
    

    成员函数

    template <typename _Tp1>
    shared_ptr &operator=(const shared_ptr<_Tp1> &__r) noexcept {
      this->__shared_ptr<_Tp>::operator=(__r);
      return *this;
    }
    

    class __shared_ptr

      template <typename _Tp, _Lock_policy _Lp> class __shared_ptr
    

    成员变量

    _Tp *_M_ptr;                     // Contained pointer.
    __shared_count<_Lp> _M_refcount; // Reference counter.
    

    成员函数

       // 重载赋值运算符 
       __shared_ptr &operator=(const __shared_ptr &) noexcept = default;
    
       template <typename _Tp1>
        __shared_ptr &operator=(const __shared_ptr<_Tp1, _Lp> &__r) noexcept {
          _M_ptr = __r._M_ptr;
          _M_refcount = __r._M_refcount; // __shared_count::op= doesn't throw
          return *this;
        }
    
    
      // 构造函数
       template <typename _Tp1>
        explicit __shared_ptr(_Tp1 *__p) : _M_ptr(__p), _M_refcount(__p) {
          __glibcxx_function_requires(
              _ConvertibleConcept<_Tp1 *, _Tp *>) static_assert(sizeof(_Tp1) > 0,
                                                                "incomplete type");
          __enable_shared_from_this_helper(_M_refcount, __p, __p);
        }
    
    	// 析构函数
        ~__shared_ptr() = default;
    
    

    class __shared_count

    成员变量

    _Sp_counted_base<_Lp> *_M_pi;//_M_pi是一个_Sp_counted_base基类对象指针,指向_Sp_counted_ptr子类对象内存,这样_M_pi内部就既可以控制引用计数,又可以在最后释放托管内存。
    

    成员函数

       // 构造函数
      template <typename _Ptr> explicit __shared_count(_Ptr __p) : _M_pi(0) {
          __try {
            _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p); //   _M_pi指向_Sp_counted_ptr子类对象内存
          }
          __catch(...) {
            delete __p;
            __throw_exception_again;
          }
        }
    
      // 析构函数
        ~__shared_count() noexcept {
          if (_M_pi != nullptr)
            _M_pi->_M_release();
        }
     
    
    
       // 重载赋值运算符
       __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;
        }
    
    
        explicit operator bool() const // never throws
        {
          return _M_ptr == 0 ? false : true;
        }
    
    

    class _Sp_counted_base

    template <_Lock_policy _Lp = __default_lock_policy>
    class _Sp_counted_base : public _Mutex_base<_Lp>
    

    成员变量

    private:
      _Atomic_word _M_use_count;  // #shared
      _Atomic_word _M_weak_count; // #weak + (#shared != 0)
    

    成员函数

     _Sp_counted_base() noexcept : _M_use_count(1), _M_weak_count(1) {}
    //注意当shared_ptr拷贝或者移动时_M_weak_count是不会增加的,它表示的是管理对象的计数,只有当__M_use_count为0时_M_weak_count才会减1,除此之外_M_weak_count的数值是由weak_ptr控制的。
    
    virtual ~_Sp_counted_base() noexcept {}
    
    
     void _M_add_ref_copy() {
          __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1);
        }
    
    
     void _M_release() noexcept {
          // Be race-detector-friendly.  For more info see bits/c++config.
          _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();
            // There must be a memory barrier between dispose() and destroy()
            // to ensure that the effects of dispose() are observed in the
            // thread that runs destroy().
            // See http://gcc.gnu.org/ml/libstdc++/2005-11/msg00136.html
            if (_Mutex_base<_Lp>::_S_need_barriers) {
              _GLIBCXX_READ_MEM_BARRIER;
              _GLIBCXX_WRITE_MEM_BARRIER;
            }
    
            // Be race-detector-friendly.  For more info see bits/c++config.
            _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 {
          // Be race-detector-friendly. For more info see bits/c++config.
          _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) {
              // See _M_release(),
              // destroy() must observe results of dispose()
              _GLIBCXX_READ_MEM_BARRIER;
              _GLIBCXX_WRITE_MEM_BARRIER;
            }
            _M_destroy();
          }
        }
    
    

    class _Sp_counted_ptr

    template <typename _Ptr, _Lock_policy _Lp>
    class _Sp_counted_ptr final : public _Sp_counted_base<_Lp>
    

    成员变量

    protected:
      _Ptr _M_ptr; // 托管内存的指针
    

    成员函数

    explicit _Sp_counted_ptr(_Ptr __p) : _M_ptr(__p) {}
    
    
     virtual void _M_dispose() noexcept { delete _M_ptr; }
    
     virtual void _M_destroy() noexcept { delete this; }
    

    weak_ptr

    参考自https://www.jianshu.com/p/b6ac02d406a0

    注意当shared_ptr拷贝或者移动时_M_weak_count是不会增加的,它表示的是管理对象的计数,只有当__M_use_count为0时_M_weak_count才会减1,除此之外_M_weak_count的数值是由weak_ptr控制的。

    weak_ptr内部其实和shared_ptr内部持有的是同一个管理对象指针,即_Sp_counted_base的指针,当weak_ptr赋值、拷贝、析构时候,_Sp_counted_base内部的_M_weak_count会相应加减。当_M_weak_count为0时,表示不再需要管理对象来控制托管对象,调用_M_destroy()的delete this来释放管理对象内存。

    weak_ptr不会延长原生指针所指向的对象的生命周期,实际上weak_ptr也没有原生指针的构造函数,只能由shared_ptr或weak_ptr进行构造。是一种不已延长生命周期为目的的只能指针。

    class weak_ptr

    template <typename _Tp> class weak_ptr : public __weak_ptr<_Tp>
    

    成员函数

    template <typename _Tp1>
    weak_ptr &operator=(const weak_ptr<_Tp1> &__r) noexcept {
      this->__weak_ptr<_Tp>::operator=(__r);
      return *this;
    }
    
     template <typename _Tp1,
                  typename = typename std::enable_if<
                      std::is_convertible<_Tp1 *, _Tp *>::value>::type>
        weak_ptr(const weak_ptr<_Tp1> &__r) noexcept : __weak_ptr<_Tp>(__r) {}
    
    
    // 用来延长生命周期
        shared_ptr<_Tp> lock() const noexcept {
    #ifdef __GTHREADS
          if (this->expired())
            return shared_ptr<_Tp>();
    
          __try {
            return shared_ptr<_Tp>(*this);
          }
          __catch(const bad_weak_ptr &) { return shared_ptr<_Tp>(); }
    #else
          return this->expired() ? shared_ptr<_Tp>() : shared_ptr<_Tp>(*this);
    #endif
        } 
    
    

    class __weak_ptr

    template <typename _Tp, _Lock_policy _Lp> class __weak_ptr
    

    成员变量

    _Tp *_M_ptr;                   // Contained pointer.
    __weak_count<_Lp> _M_refcount; // Reference counter.
    

    成员函数

      template <typename _Tp1,
                  typename = typename std::enable_if<
                      std::is_convertible<_Tp1 *, _Tp *>::value>::type>
        __weak_ptr(const __shared_ptr<_Tp1, _Lp> &__r) noexcept
            : _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount) {}
    
    
    template <typename _Tp1>
    __weak_ptr &operator=(const __weak_ptr<_Tp1, _Lp> &__r) noexcept {
      _M_ptr = __r.lock().get();
      _M_refcount = __r._M_refcount;
      return *this;
    }
    
    __weak_ptr(const __weak_ptr &) noexcept = default;
    
     ~__weak_ptr() = default;
    
    bool expired() const noexcept {
      return _M_refcount._M_get_use_count() == 0;
    }
    

    class __weak_count

    template <_Lock_policy _Lp> class __weak_count
    

    成员变量

    _Sp_counted_base<_Lp> *_M_pi;
    

    成员函数

    __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<_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;
     }
    
     ~__weak_count() noexcept {
          if (_M_pi != 0)
            _M_pi->_M_weak_release();
     }
    
    展开全文
  • c++11 weak_ptr 源码分析

    2021-09-17 11:33:33
    写在最前。。。 0. 前言 所谓智能指针,可以从字面上理解为“智能”的指针。...c++11 中发布了shared_ptr、unique_ptrweak_ptr 用以资源的管理,都是定义在memory 这个头文件中。 std::shared_ptr 允.

    写在最前。。。

     

    请支持原创~~ 

     

    0. 前言

    所谓智能指针,可以从字面上理解为“智能”的指针。具体来讲,智能指针和普通指针的用法是相似的,不同之处在于,智能指针可以在适当时机自动释放分配的内存。也就是说,使用智能指针可以很好地避免“忘记释放内存而导致内存泄漏”问题出现。由此可见,C++ 也逐渐开始支持垃圾回收机制了,尽管目前支持程度还有限。

    c++11 中发布了shared_ptrunique_ptrweak_ptr 用以资源的管理,都是定义在memory 这个头文件中。

    • std::shared_ptr 允许多个shared_ptr 实例指向同一个对象,通过计数管理;
    • std::unique_ptr 是独占对象;
    • weak_ptr 是辅助类,是一种弱引用,指向shared_ptr 所管理的对象。

    1. 源码分析

    1.1 头文件

    #include <memory>

    1.2 构造

        constexpr weak_ptr() noexcept;
        template<class Y> weak_ptr(shared_ptr<Y> const& r) noexcept;
        weak_ptr(weak_ptr const& r) noexcept;
        template<class Y> weak_ptr(weak_ptr<Y> const& r) noexcept;
        weak_ptr(weak_ptr&& r) noexcept;                      // C++14
        template<class Y> weak_ptr(weak_ptr<Y>&& r) noexcept; // C++14

    构造函数相比shared_ptr 和unique_ptr 少了很多,其中移动构造函数是c++14 发布的。

    另外,weak_ptr 中的对象只能通过shared_ptr 或 另一个weak_ptr 实例获取。

    1.2.1 weak_ptr 的移动构造函数

    template<class _Tp>
    inline
    weak_ptr<_Tp>::weak_ptr(weak_ptr&& __r) _NOEXCEPT
        : __ptr_(__r.__ptr_),
          __cntrl_(__r.__cntrl_)
    {
        __r.__ptr_ = 0;
        __r.__cntrl_ = 0;
    }

    同shared_ptr,weak_ptr 中存放一个对象的指针__ptr_ 和用以计数的__cntrl_。

    注意的是__cntrl_ 虽然是可以从shared_ptr 中获取(构造参数为shared_ptr时),但是weak_ptr 并不会增加对象指针的shared 引用计数,而只是增加weak 计数。

    1.2.2  weak_ptr 的拷贝构造函数

    template<class _Tp>
    inline
    weak_ptr<_Tp>::weak_ptr(weak_ptr const& __r) _NOEXCEPT
        : __ptr_(__r.__ptr_),
          __cntrl_(__r.__cntrl_)
    {
        if (__cntrl_)
            __cntrl_->__add_weak();
    }

    会通过__cntrl_ 调用add_weak,而不是add_shared。

    下面举例shared_ptr 通常的创建方式:

    std::weak_ptr<int> wp1;                  //不传入任何实参
    
    std::shared_ptr<int> sp (new int);
    std::weak_ptr<int> wp3 (sp);             //没有直接传对象指针的构造,只能通过shared_ptr 或者weak_ptr

    1.3 赋值重载

        weak_ptr& operator=(weak_ptr const& r) noexcept;
        template<class Y> weak_ptr& operator=(weak_ptr<Y> const& r) noexcept;
        template<class Y> weak_ptr& operator=(shared_ptr<Y> const& r) noexcept;
        weak_ptr& operator=(weak_ptr&& r) noexcept;                      // C++14
        template<class Y> weak_ptr& operator=(weak_ptr<Y>&& r) noexcept; // C++14

    1.4 修改的接口

        void swap(weak_ptr& r) noexcept;
        void reset() noexcept;
    

    1.5 获取

        long use_count() const noexcept;
        bool expired() const noexcept;
        shared_ptr<T> lock() const noexcept;
        template<class U> bool owner_before(shared_ptr<U> const& b) const noexcept;
        template<class U> bool owner_before(weak_ptr<U> const& b) const noexcept;

    对于weak_ptr 的成员函数总结如下:

    成员方法

    功 能

    operator=()

    重载 = 赋值运算符,是的 weak_ptr 指针可以直接被 weak_ptr 或者 shared_ptr 类型指针赋值。

    swap(x)

    其中 x 表示一个同类型的 weak_ptr 类型指针,该函数可以互换 2 个同类型 weak_ptr 指针的内容。

    reset()

    将当前 weak_ptr 指针置为空指针。

    use_count()

    查看指向和当前 weak_ptr 指针相同的 shared_ptr 指针的数量。

    expired()

    判断当前 weak_ptr 指针为否过期(指针为空,或者指向的堆内存已经被释放)。

    lock()

    如果当前 weak_ptr 已经过期,则该函数会返回一个空的 shared_ptr 指针;反之,该函数返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针。

    注意:

    •  weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源;
    •  weak_ptr用于解决”引用计数”模型循环依赖问题,weak_ptr指向一个对象,并不增减该对象的引用计数器。weak_ptr用于配合shared_ptr使用,并不影响动态对象的生命周期,即其存在与否并不影响对象的引用计数器。weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象。weak_ptr提供了expired()与lock()成员函数,前者用于判断weak_ptr指向的对象是否已被销毁,后者返回其所指对象的shared_ptr智能指针(对象销毁时返回”空”shared_ptr);
    • 智能指针是模板类而不是指针;
    • weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例进而访问原始对象
    •  weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shard_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放;
    •  当创建一个weak_ptr时,要用一个shared_ptr来初始化它。不能使用weak_ptr直接访问对象,而必须调用lock。此函数检查weak_ptr指向的对象是否仍存在。如果存在,lock返回一个指向共享对象的shared_ptr。与任何其它shared_ptr类似,只要此shared_ptr存在,它所指向的底层对象也就会一直存在;

    2. 举例

    #include <iostream>
    #include <memory>
     
    std::weak_ptr<int> gw;
     
    void observe()
    {
        std::cout << "use_count == " << gw.use_count() << ": ";
        if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
    	    std::cout << *spt << "\n";
        }
        else {
            std::cout << "gw is expired\n";
        }
    }
     
    int main()
    {
        {
            auto sp = std::make_shared<int>(42);
    	    gw = sp;
     
    	    observe();
        }
     
        observe();
    }

    运行结果:

    use_count == 1: 42
    use_count == 0: gw is expired

    在区域执行完成后,对象会被释放,虽然gw 还在,但是通过lock 获取的shared_ptr 已经为空

    再来看个例子:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main()
    {
        std::shared_ptr<int> sp1(new int(10));
        std::shared_ptr<int> sp2(sp1);
        std::weak_ptr<int> wp(sp2);
        //输出和 wp 同指向的 shared_ptr 类型指针的数量
        cout << wp.use_count() << endl;
        //释放 sp2
        sp2.reset();
        cout << wp.use_count() << endl;
        //借助 lock() 函数,返回一个和 wp 同指向的 shared_ptr 类型指针,获取其存储的数据
        cout << *(wp.lock()) << endl;
        return 0;
    }

    运行结果:

    2
    1
    10

    展开全文
  • 智能指针是存储指向动态分配(堆)对象的类,用于生存期控制,能够确保在离开指针所在作用...C++11 提供了 3 种智能指针:std::shared_ptr,std::uniq_ptr,std::weak_ptr,使用时需要引用头文件<memory>。 ...


    前言

    智能指针(Smart Pointer)是存储指向动态分配(堆)对象的类,用于生存期控制,能够确保在离开指针所在作用域时,自动正确地销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数。每使用它一次,内部的引用计数加1,每析构一次,内部引用计数减1,减为0时,删除所指向的堆内存。
    C++11 提供了 3 种智能指针:std::shared_ptr,std::uniq_ptr,std::weak_ptr,使用时需要引用 memory 头文件。


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

    std::shared_ptr使用引用计数,每一个 std::shared_ptr 的拷贝都指向相同的内存。在最后一个 std::shared_ptr 析构的时候,内存才会被释放。。

    1.1 std::shared_ptr 的基本用法

    1. 初始化

    可以通过构造函数、std::make_shared 辅助函数和 reset 方法来初始化 shared_ptr,代码如下:

    //=============== std::shared_ptr 的初始化 ==================
    	std::shared_ptr<int> p(new int(1));
    	std::shared_ptr<int> p2 = p;
    	std::shared_ptr<int> ptr;
    	ptr.reset(new int(1));
    	if (ptr) {
    		cout << "shared ptr is initialized.";
    	}
    

    我们应该优先使用 std::make_shared 来构造智能指针,因为它更高效。

    2. 获取原始指针

    当需要获取原始指针时,可以通过 get 方法来返回原始指针,代码如下:

    std::shared_ptr<int> ptr(new int(1));
    int* p = ptr.get();
    

    3. 指定删除器

    智能指针初始化可以指定删除器,代码如下:

    void DeleteIntPtr(int* p) {
    	delete p;
    }
    std::shared_ptr<int> p(new int, DeleteIntPtr);
    

    当 p 的引用计数为 0 时,自动调用删除器 DeleteIntPtr 来释放对象的内存。删除器可以是一个 lambda表达式,因此,上面的写法还可以改为:

    std::shared_ptr<int> p(new int, [](int* p){delete p;});
    

    当我们用 shared_ptr 管理动态数组时,需要指定删除器,因为 std::shared_ptr 的默认删除器不支持数组对象,代码如下:

    std::shared_ptr<int> p(new int[10], [](int* p){delete[] p;});  //指定 delete[]
    

    也可以将 std::default_delate 作为删除器。default_delate 的内部是通过调用 delete 来实现功能的,代码如下:

    std::shared_ptr<int> p(new int[10], std::default_delete<int[]>);  
    

    另外,还可以通过封装一个 make_shared_array 方法来让 shared_ptr 支持数组,代码如下:

    template<typename T>
    shared_ptr<T> make_shared_array(size_t size) {
    	return shared_ptr<T>(new T[size], default_delete<T[]>());
    }
    
    //test
    std::shared_ptr<int> p = make_shared_array<int>(10);
    

    1.2 使用 shared_ptr 需要注意的问题

    1)不要用一个原始指针初始化多个 shared_ptr,例如下面这样就是错误的:

    int *ptr = new int;
    std::shared_ptr<int> p1(ptr);
    std::shared_ptr<int> p2(ptr);   //logic error
    

    2) 不要在函数实参中创建 shared_ptr,

    function (shared_ptr<int>(new int), g());  //有缺陷
    

    因为 C++ 的函数参数的计算顺序在不同的编译器上调用可能是不一样的。一般是从右到左,但也有可能是从左到右。所以可能的过程是先 new int,然后调用 g(),如果恰好 g() 发生异常,而 shared_ptr 还没有创建,则 int 内存泄漏了,正确的写法应该是先创建智能指针,代码如下:

    shared_ptr<int> p(new int());
    f(p, g());
    

    3)不要将 this 指针通过 shared_ptr 返回出来,因为 this 本质上是一个裸指针,这样会导致重复析构。通过 shared_from_this 返回 this 指针。

    struct A 
    {
    	shared_ptr<A>GetSelf()
    	{
    		return shared_ptr<S>(this);   //don't do this;
    	}
    };
    
    int main()
    {
    	shared_ptr<A> sp1(new A);
    	shared_ptr<A> sp2 = sp1->GetSelf();
    	return 0;
    }
    

    在这个例子中,由于用同一个指针(this)构造了两个智能指针 sp1 和 sp2,而它们之间是没有任何关系的,在离开作用域之后 this 将会被构造的两个智能指针各自析构,导致重复析构的错误。
    正确返回 this 的shared_ptr 的做法是:让目标类通过派生 std::enable_shared_from_this 类,然后使用基类的成员函数 shared_from_this 来返回 this 的 shared_ptr,示例如下:

    class A : public std::enable_shared_from_this<A>
    {
    	std::shared_ptr<A> GetSelf()
    	{
    		return shared_from_this();
    	}
    };
    std::shared_ptr<A> spy(new A);
    std::shared_ptr<A> p = spy->GetSelf();     //OK
    
    • 避免循环引用,智能指针最大的一个陷阱是循环引用,循环引用会导致内存泄漏。
    struct A;
    struct B;
    
    struct A {
    	std::shared_ptr<B> bptr;
    	~A() {
    		cout << "A is deleted!" << endl;
    	}
    };
    
    struct B {
    	std::shared_ptr<A> aptr;
    	~B() {
    		cout << "B is deleted!" << endl;
    	}
    };
    
    void testPtr() {
    	std::shared_ptr<A> ap(new A);
    	std::shared_ptr<B> bp(new B);
    	ap->bptr = bp;
    	bp->aptr = ap;
    }
    

    测试结果是两个指针A 和B都不会被删除,存在内存泄漏。循环引用导致 ap 和 bp 的引用计数为 2,在离开作用域之后,ap 和 bp 的引用计数减为1,并不会为 0,导致两个指针都不会被析构,产生了内存泄漏。解决办法是把 A 和 B 任何一个成员变量改为 weak_ptr。

    二、unique_ptr 独占的智能指针

    1. unique_ptr 基本用法

    1.1 unique_ptr 的移动

    unique_ptr 是一个独占型的智能指针,它不允许其他的智能指针共享其内容的指针,不允许通过赋值将一个 unique_ptr 赋值给另外一个 unique_ptr。下面用法是错误的:

    unique_ptr<T> myPtr(new T);
    unique_ptr<T> myOtherPtr = myPtr;    //错误,不能赋值
    

    unique_ptr 不允许复制,但可以通过函数返回给其他的 unique_ptr,还可以通过 std::move 来转移到其他的 unique_ptr,这样它本身就不再拥有原来指针的所有权了,例如:

    unique_ptr<T> myPtr(new T);
    unique_ptr<T> myOtherPtr = std::move(myPtr);  //OK
    unique_ptr<T> ptr = myPtr;                    //错误,只能移动,不可复制
    

    unique_ptr 不像 shared_ptr 可以通过 make_shared 方法来创建智能指针,C++11 目前还没有提供 make_unique 方法。

    1.2 指定数组

    unique_ptr 和 shared_ptr 相比,unique_ptr 除了独占这个特点之外,还可以指向一个数组:

    std::unique_ptr<int []> ptr(new int[10]);
    ptr[9] = 9;
    
    //而下面的写法是不合法的
    std::shared_ptr<int []> ptr(new int[10]);    //不合法
    

    1.3 指定删除器

    unique_ptr 指定删除器和 shared_ptr 是有差别的,比如:

    std::shared_ptr<int> ptr(new int(1), [](int* p){delete p;});   //正确
    std::unique_ptr<int> ptr(new int(1), [](int* p){delete p;});   //错误
    

    std::unique_ptr 指定删除器的时候需要确定删除器的类型,所以不能像 shared_ptr 那样直接指定删除器,可以这样写:

    std::unique_ptr<int, void(*)(int*)> ptr(new int(1), [](int *p) {delete p;}); 
    

    上面这种写法在 lambda 没有捕获变量的情况下是正确的,如果捕获了变量,则会编译报错:

    std::unique_ptr<int, void(*)(int*)> ptr(new int(1), [&](int *p) {delete p;}); //错误,捕获了变量
    

    这是因为 lambda 在没有捕获变量的情况下是可以直接转换为函数指针的,一旦捕获了就无法转换了。如果希望 unique_ptr 的删除器支持 lambda,可以这样写:

    std::unique_ptr<int, std::function<void(int*)>> ptr(new int(1), [&](int *p){delete p;});
    

    还可以自定义 unique_ptr 的删除器,比如下面的代码:

    #include <memory>
    #include <functional>
    using namespace std;
    
    struct MyDeleter
    {
    	void operator()(int* p)
    	{
    		cout<<"delete"<<endl;
    		delete p;
    	}
    };
    
    int main()
    {
    	std::unique_ptr<int, MyDeleter> p(new int(1));
    	return 0;
    }
    

    关于 shared_ptr 和 unique_ptr 的使用场景要根据实际应用需求来选择,如果希望只有一个智能指针管理资源或者管理数组就用 unique_ptr,如果希望多个智能指针管理同一个资源就用 shared_ptr。

    三、weak_ptr 弱引用的智能指针

    弱引用指针 weak_ptr 是用来监视 shared_ptr 的,不会使引用计数加1,它不管理 shared_ptr 内部的指针,主要是为了监视 shared_ptr 的生命周期,更像是 shared_ptr 的一个助手。weak_ptr 没有重载操作符*和->,因为它不共享指针,不能操作资源,主要是为了通过 shared_ptr 获得资源的检测权,它的构造不会增加引用计数,它的析构也不会减少引用计数,纯粹只是作为一个旁观者来监视 shared_ptr 中管理的资源是否存在。weak_ptr 还可以用来返回 this 指针和解决循环引用的问题。

    1. weak_ptr 基本用法

    1)通过 use_count() 方法获得当前观测资源的引用计数,代码如下:

    shared_ptr<int> sp(new int(10));
    weak_ptr<int> wp(sp);
    cout<<wp.use_count()<<endl;    //结果输出1
    

    2)通过 expired() 方法判断所检测的资源是否已经被释放,代码如下:

    shared_ptr<int> sp(new int(10));
    weak_ptr<int> wp(sp);
    if (wp.expired())
    {
    	std::cout << "weak_ptr 无效,所监测的智能指针已被释放\n";
    }
    else
    {
    	std::cout << "weak_ptr 有效\n";
    }
    
    //结果输出: weak_ptr 有效
    

    3)通过 lock 方法来获取所监视的 shared_ptr,代码如下:

    std::weak_ptr<int> gw;
    void f()
    {
    	if (gw.expired())
    	{
    		std::cout << "gw is expired\n";
    	}
    	else
    	{
    		auto spt = gw.lock();
    		std::cout << *spt << "\n";
    	}
    }
    
    int main()
    {
    	{
    		auto sp = std::make_shared<int>(42);
    		gw = sp;
    		f();
    	}
    	f();
    	return 0;
    }
    
    //输出如下:
    42
    gw is expired
    

    2. weak_ptr 返回 this 指针

    上面提到不能将 this 指针返回为 shared_ptr,需要通过派生 std::enable_shared_from_this 类,并通过其方法 shared_from_this 来返回智能指针,原因是 std::enable_shared_from_this 类中有一个 weak_ptr,这个 weak_ptr 用来观测 this 智能指针,调用 shared_from_this() 方法时,会调用内部这个 weak_ptr 的 lock() 方法,将所观测的 shared_ptr 返回。再看一下前面的例子:

    struct A : public std::enable_shared_from_this<A>
    {
    	std::shared_ptr<A> GetSelf()
    	{
    		return shared_from_this();
    	}
    
    	~A()
    	{
    		cout << "A is deleted" << endl;
    	}
    };
    
    int main()
    {
    	std::shared_ptr<A> spy(new A);
    	std::shared_ptr<A> p = spy->GetSelf();
    	return 0;
    }
    
    //输出结果: A is deleted
    

    在外面创建 A 对象的智能指针和通过该对象返回 this 的智能指针都是安全的,因为 shared_from_this() 是内部的 weak_ptr 调用 lock() 方法之后返回的智能指针,在离开作用域后,spy 的引用计数减为0,A 对象会被析构,不会出现 A 对象被析构两次的问题。

    需要注意的是,获取自身智能指针的函数仅在 shared_ptr 的构造函数被调用之后才能使用,因为 enable_shared_from_this 内部的 weak_ptr 只有通过 shared_ptr 才能构造。

    3. weak_ptr 解决循环引用的问题

    上面提到因为智能指针的循环引用导致内存泄漏的问题,再看一下其示例:

    struct A;
    struct B;
    
    struct A {
    	std::shared_ptr<B> bptr;
    	~A() {
    		cout << "A is deleted!" << endl;
    	}
    };
    
    struct B {
    	std::shared_ptr<A> aptr;
    	~B() {
    		cout << "B is deleted!" << endl;
    	}
    };
    
    void testPtr() {
    	std::shared_ptr<A> ap(new A);
    	std::shared_ptr<B> bp(new B);
    	ap->bptr = bp;
    	bp->aptr = ap;
    	std::cout << "ap use count: " << ap.use_count() << endl;
    	std::cout << "bp use count: " << bp.use_count() << endl;
    }
    
    int main()
    {
    	testPtr();
    	return 0;
    }
    
    //输出如下:
    ap use count: 2
    bp use count: 2
    

    在这个例子中由于循环引用导致 ap 和 bp 的引用计数都为2,离开作用域之后引用计数减为1,不会去删除指针,导致内存泄漏。通过 weak_ptr 就可以解决这个问题,只要将 A 或 B 的任意一个成员变量改为 weak_ptr 即可。

    struct A;
    struct B;
    
    struct A {
    	std::shared_ptr<B> bptr;
    	~A() {
    		cout << "A is deleted!" << endl;
    	}
    };
    
    struct B {
    	//std::shared_ptr<A> aptr;
    	std::weak_ptr<A> aptr;   //改为 weak_ptr
    	~B() {
    		cout << "B is deleted!" << endl;
    	}
    };
    
    void testPtr() {
    	std::shared_ptr<A> ap(new A);
    	std::shared_ptr<B> bp(new B);
    	ap->bptr = bp;
    	bp->aptr = ap;
    	std::cout << "ap use count: " << ap.use_count() << endl;
    	std::cout << "bp use count: " << bp.use_count() << endl;
    }
    
    
    //输出结果:
    ap use count: 1
    bp use count: 2
    A is deleted!
    B is deleted!
    

    这样在对 B 的成员赋值时,即执行 bp->aptr = ap; 时, 由于 aptr 是 weak_ptr,它并不会增加引用计数,所以 ap 的引用计数仍然是1,在离开作用域之后,ap 的引用计数会减为0,A指针会被析构,析构后其内部的 bptr 的引用计数减为1,然后在离开作用域后 bp 引用计数又从1减为0,B对象也会被析构,不会发生内存泄露。


    展开全文
  • 总体而言,实现shared_ptr、weak_ptr智能指针是通过两个基类_Ref_count_base、_Ptr_base以及它们的派生类来实现的,其结构图如下: 1、抽象类_Ref_count_base class _Ref_count_base { // common code for ...

    智能指针原理剖析(一):auto_ptr、unique_ptr

    shared_ptr、weak_ptr原理剖析

    关于shared_ptr源码剖析的博客有很多,推荐一篇讲解十分详细的博客:从源码理解智能指针(二)—— shared_ptr、weak_ptr。本文在此基础上,对shared_ptr、weak_ptr实现的原理进行总结并画出类关系图,读者可结合两篇博客一起阅读,从而帮助理解。

    总体而言,实现shared_ptr、weak_ptr智能指针是通过两个基类_Ref_count_base、_Ptr_base以及它们的派生类来实现的,其结构图如下:
    在这里插入图片描述

    1、抽象类_Ref_count_base

    class _Ref_count_base
    	{	// common code for reference counting
    private:
    	virtual void _Destroy() = 0;
    	virtual void _Delete_this() = 0;
    
    private:
    	_Atomic_counter_t _Uses;  //强引用计数
    	_Atomic_counter_t _Weaks;  //弱引用计数
    
    protected:
    	_Ref_count_base()
    		{	// construct
    		_Init_atomic_counter(_Uses, 1);  //强引用计数初始化为1
    		_Init_atomic_counter(_Weaks, 1); //弱引用计数初始化为1
    		}
    
    public:
    	virtual ~_Ref_count_base() _NOEXCEPT  //虚析构函数
    		{	// ensure that derived classes can be destroyed properly
    		}
    
    	unsigned int _Get_uses() const  //获取强引用计数
    		{	// return use count
    		return (_Get_atomic_count(_Uses));
    		}
    		
    	void _Incref()
    		{	// increment use count
    		_MT_INCR(_Mtx, _Uses);// _Uses+1
    		}
     
    	void _Incwref()
    		{	// increment weak reference count
    		_MT_INCR(_Mtx, _Weaks);//_Weaks+1
    		}
    		
    	void _Decref()
    		{	// decrement use count
    		if (_MT_DECR(_Mtx, _Uses) == 0)
    			{	// destroy managed resource, decrement weak reference count
    			_Destroy(); //释放资源
    			_Decwref();
    			}
    		}
    
    	void _Decwref()
    		{	// decrement weak reference count
    		if (_MT_DECR(_Mtx, _Weaks) == 0)  //判断弱引用计数-1后是否为0
    			_Delete_this(); //删除计数器自身
    		}
    
    	long _Use_count() const  //返回强引用计数
    		{	// return use count
    		return (_Get_uses());
    		}
    
    	bool _Expired() const    //强引用计数是否为0
    		{	// return true if _Uses == 0
    		return (_Get_uses() == 0);
    		}
    
    	virtual void *_Get_deleter(const _XSTD2 type_info&) const
    		{	// return address of deleter object
    		return (0);
    		}
    	};
    

    _Ref_count_base是派生类_Ref_count、_Ref_count_del、_Ref_count_del_alloc的基类,是计数器的接口。基类中纯虚函数virtual void _Destroy() = 0、virtual void _Delete_this() = 0是实现多态的接口,具体定义在三个子类中实现,分别表示释放资源、删除计数器自身。

    _Uses是强引用计数,计算引用资源的shared_ptr个数,_Weaks是弱引用计数,计算引用资源的weak_ptr的个数。 _Uses和_Weaks的增加或减少,在底层中通过一条指令就可以实现,是原子操作

    类中封装了函数_Decref(),表示:若 引用计数 _Uses == 0,就调用_Destroy()来释放资源,并调用_Decwref()递减_Weaks。

    类中封装了函数_Decwref(),表示:若弱引用计数_Weaks= =0,就调用_Delete_this()来删除计数器自身。
    由_Ref_count_base类的源码可知,释放资源的条件为: _Uses== 0;删除计数器的条件为:_Uses== 0&&_Weaks==0。

    1.1、派生类_Ref_count

    template<class _Ty>
    	class _Ref_count
    	: public _Ref_count_base
    	{	// handle reference counting for object without deleter
    public:
    	_Ref_count(_Ty *_Px)
    		: _Ref_count_base(), _Ptr(_Px)
    		{	// construct
    		}
    
    private:
    	virtual void _Destroy()
    		{	// destroy managed resource
    		delete _Ptr;
    		}
    
    	virtual void _Delete_this()
    		{	// destroy self
    		delete this;
    		}
    
    	_Ty * _Ptr;
    	};
    

    Ref_count是_Ref_count_base的派生类,定义了资源释放函数_Destroy() 、删除计数器自身函数_Delete_this(),以及一个指向_Ty类型的指针_Ptr,由派生类_Ref_count生成对象时只能通过指针_Ptr来进行构造。

    1.2、派生类_Ref_count_del

    template<class _Ty,
    	class _Dx>
    	class _Ref_count_del
    	: public _Ref_count_base
    	{	// handle reference counting for object with deleter
    public:
    	_Ref_count_del(_Ty *_Px, _Dx _Dt)
    		: _Ref_count_base(), _Ptr(_Px), _Dtor(_Dt)
    		{	// construct
    		}
    
    	virtual void *_Get_deleter(const _XSTD2 type_info& _Typeid) const
    		{	// return address of deleter object
    		return ((void *)(_Typeid == typeid(_Dx) ? &_Dtor : 0));
    		}
    
    private:
    	virtual void _Destroy()
    		{	// destroy managed resource
    		_Dtor(_Ptr);
    		}
    
    	virtual void _Delete_this()
    		{	// destroy self
    		delete this;
    		}
    
    	_Ty * _Ptr;
    	_Dx _Dtor;	// the stored destructor for the controlled object 被控制对象的已析构函数
    	};
    

    Ref_count_del是_Ref_count_base的派生类,定义了资源释放函数_Destroy() 、删除计数器自身函数_Delete_this(),以及一个指向_Ty类型的指针_Ptr和_Dx类型的删除器_Dtor,由派生类_Ref_count生成对象时只能通过指针_Ptr和删除器_Dtor来进行构造。

    1.3、派生类_Ref_count_del_alloc

    template<class _Ty,
    	class _Dx,
    	class _Alloc>
    	class _Ref_count_del_alloc
    	: public _Ref_count_base
    	{	// handle reference counting for object with deleter and allocator
    public:
    	typedef _Ref_count_del_alloc<_Ty, _Dx, _Alloc> _Myty;
    	typedef typename _Alloc::template rebind<_Myty>::other _Myalty;
    
    	_Ref_count_del_alloc(_Ty *_Px, _Dx _Dt, _Myalty _Al)
    		: _Ref_count_base(), _Ptr(_Px), _Dtor(_Dt), _Myal(_Al)
    		{	// construct
    		}
    
    	virtual void *_Get_deleter(const _XSTD2 type_info& _Typeid) const
    		{	// return address of deleter object
    		return ((void *)(_Typeid == typeid(_Dx) ? &_Dtor : 0));
    		}
    
    private:
    	virtual void _Destroy()
    		{	// destroy managed resource
    		_Dtor(_Ptr);
    		}
    
    	virtual void _Delete_this()
    		{	// destroy self
    		_Myalty _Al = _Myal;
    		_Al.destroy(this);
    		_Al.deallocate(this, 1);
    		}
    
    	_Ty * _Ptr;
    	_Dx _Dtor;	// the stored destructor for the controlled object
    	_Myalty _Myal;	// the stored allocator for this
    	};
    

    Ref_count_del_alloc是_Ref_count_base的派生类,定义了资源释放函数_Destroy() 、删除计数器自身函数_Delete_this(),以及一个指向_Ty类型的指针_Ptr、_Dx类型的删除器_Dtor、_Myalty类型的分配器_Myal,由派生类_Ref_count生成对象时只能通过指针_Ptr、删除器_Dtor和分配器_Myal来进行构造。

    2、基类_Ptr_base

    基类_Ptr_base中含有两个成员变量,一个指向_Ty类型的指针_Ptr、一个指向_Ref_count_base类型的计数器指针_Rep。此处的指针_Ptr应与类_Ref_count_base下派生类中的_Ptr相同。
    _Ptr_base中的主要函数如下:

        _Ptr_base()  //无参构造函数
        _Ptr_base(_Myt&& _Right)    //移动构造函数
        template<class _Ty2> _Ptr_base(_Ptr_base<_Ty2>&& _Right)  //移动构造函数
        _Myt& operator=(_Myt&& _Right)   //移动赋值函数
        long use_count() const _NOEXCEPT  //获取强引用计数
        void  _Decref()  //调用_Rep->_Decref(),减少计数器的强引用计数
        void  _Decwref()  //调用_Rep->_Decwref(),减少计数器的弱引用计数
        void _Reset()及其重载版本   //重置当前shared_ptr,减少当前对象持有某资源的强引用计数,增加传入对象持有新资源的强引用计数
        void _Resetw()及其重载版本    //重置当前weak_ptr,减少当前对象持有某资源的弱引用计数,增加传入对象持有新资源的弱引用计数
    

    3、shared_ptr

    shared_ptr是基类_Ptr_base的派生类,也是用户生成共享智能指针的接口。shared_ptr封装了大量的构造函数、赋值函数,主要供用户构造对象使用:

    传参构造函数

      shared_ptr() _NOEXCEPT  //无参构造函数,调用基类默认构造函数,使_Ptr和_Rep均初始化为0
      template<class _Ux> explicit shared_ptr(_Ux *_Px)    //用资源指针_Px构造,调用_Ref_count,构造完成后强引用计数为1
      template<class _Ux,class _Dx> shared_ptr(_Ux *_Px, _Dx _Dt)//用资源指针和删除器来构造,调用_Ref_count_del,构造完成后强引用计数为1
      template<class _Dx> shared_ptr(nullptr_t, _Dx _Dt) //用nullptr和删除器来构造,调用_Ref_count_del,构造完成后强引用计数为1
      template<class _Dx,class _Alloc> shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax) //用nullptr、删除器、分配器来构造,调用_Ref_count_del_alloc,构造完成后强引用计数为1
      template<class _Ux,class _Dx,class _Alloc> shared_ptr(_Ux *_Px, _Dx _Dt, _Alloc _Ax) //用不同类型的资源指针、删除器及分配器来构造,调用_Ref_count_del_alloc,构造完成后强引用计数为1
    

    以上构造函数为传参构造函数:用于从无到有构造一个shared_ptr对象,同时生成一个计数器对象,构造完成后的引用计数和弱引用计数均为1

    拷贝构造函数

     template<class _Ty2> shared_ptr(const shared_ptr<_Ty2>& _Right, _Ty *_Px) _NOEXCEPT  //传入shared_ptr对象和新的资源指针对构造对象,_Right的引用计数不会改变
     shared_ptr(const _Myt& _Other) _NOEXCEPT  //拷贝构造,传入shared_ptr对象,当前对象绑定_Other的资源指针和计数器,引用计数+1
     template<class _Ty2> explicit shared_ptr(const weak_ptr<_Ty2>& _Other,bool _Throw = true)  //用weak_ptr对象来构造shared_ptr,若成功,二者共享资源指针以及计数器,强引用计数+1,否则抛出异常
     template<class _Ty2> shared_ptr(const shared_ptr<_Ty2>& _Other, const _Const_tag& _Tag) //传入shared_ptr对象来构造,第二个参数指定使用_Const_tag转换
     template<class _Ty2> shared_ptr(const shared_ptr<_Ty2>& _Other, const _Dynamic_tag& _Tag) 传入shared_ptr对象来构造,第二个参数指定使用Dynamic_tag转换
    

    以上构造函数均为拷贝构造函数:均调用_Ptr_base中的_Reset函数,即用现有的对象来构造新的对象,共享资源和计数器,且构造完成后强引用计数+1

    移动构造函数

    template<class _Ty2> shared_ptr(auto_ptr<_Ty2>&& _Other)  //传入auto_ptr临时对象,构造完成后,临时对象销毁
    shared_ptr(_Myt&& _Right) _NOEXCEPT  //传入shared_ptr临时对象,构造完成后,临时对象销毁
    template<class _Ty2, class = typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,void>::type> shared_ptr(shared_ptr<_Ty2>&& _Right) _NOEXCEPT  //传入不同类型的shared_ptr临时对象,构造完成后,临时对象销毁
    template<class _Ux,class _Dx>	shared_ptr(unique_ptr<_Ux, _Dx>&& _Right) 传入unique_ptr临时对象,获取其资源指针以及删除器,并将_Right的资源指针设置为NULL
    

    以上构造函数均为移动构造函数:用一个临时对象来构造新的对象,继承其资源和计数器,构造完成销毁临时对象,故引用计数不变

    赋值函数

    template<class _Ux,class _Dx> _Myt& operator=(unique_ptr<_Ux, _Dx>&& _Right)  //传入unique_ptr临时对象,赋值给当前shared_ptr对象,_Right的引用计数不变,自身的引用计数-1
    _Myt& operator=(_Myt&& _Right) _NOEXCEPT  //传入shared_ptr临时对象,赋值完成后,_Right的引用计数不变,当前对象的引用计数-1
    template<class _Ty2> _Myt& operator=(shared_ptr<_Ty2>&& _Right) _NOEXCEPT  //传入不同类型的shared_ptr临时对象,赋值完成后,临时对象销毁
    _Myt& operator=(const _Myt& _Right) _NOEXCEPT  //传入shared_ptr对象,可以是临时对象也可以是非临时对象,_Right的引用计数+1,当前对象的引用计数-1
    template<class _Ty2> _Myt& operator=(const shared_ptr<_Ty2>& _Right) _NOEXCEPT  //传入不同类型的shared_ptr对象,_Right的引用计数+1,当前对象的引用计数-1
    template<class _Ty2> _Myt& operator=(auto_ptr<_Ty2>&& _Right)  //传入不同类型的auto_ptr对象,_Right的引用计数不变,当前对象的计数器引用计数-1
    

    由上述赋值函数可知,若为移动赋值函数,传入的对象为临时对象,赋值完成后,临时对象持有资源的引用计数不发生不变化(移动对象std::move),当前对象持有资源的引用计数-1;若为拷贝赋值函数,赋值完成后,传入对象的引用计数+1,当前对象持有资源的引用计数-1

    析构函数

    ~shared_ptr() _NOEXCEPT
    {	// release resource
    	this->_Decref(); //如果引用计数为0,则在计数器类中进行资源释放
    }
    

    某个shared_ptr生命期结束时,会自动调用析构函数,析构函数中会调用计数器的_Decref()函数,资源的强引用计数-1,若强引用计数为0,就会调用_Destroy() 函数释放资源。

    综上所述,智能指针shared_ptr的原理为:所有shared_ptr对象中通过同一个计数器来计算资源的引用计数_Uses,可通过拷贝构造函数、移动构造函数、赋值函数来增加新的shared_ptr指向该对象,并自动调用计数器递增引用计数。当某个shared_ptr生命期结束时,会自动调用析构函数,析构函数中会递减它所指向对象的引用计数,若强引用计数为0,析构函数就会调用计数器的_Destroy() 函数释放内存资源。

    shared_ptr线程安全

    (1)shared_ptr对象的线程安全: 同一个shared_ptr对象被多个线程写不是线程安全的,需要加锁;
    (2)计数器的线程安全: 共享引用计数的不同的shared_ptr对象,其引用计数的增加或减少被多个线程写是线程安全的,因为引用计数的增加或减小是原子操作。

    shared_ptr应用场景

    (1)多个对象共享同一个资源,对象指向资源的创建与销毁是分离的;
    (2)容器中存储动态对象。 假设程序中定义一个容器为vector<int*> v;int * p=new int(4);v.push_back( p);,当程序结束对应容器的生命期也结束时,vector容器会发生析构从而释放其中的元素p,但是并不会释放p所指向的内存资源,就会造成内存泄露。当将容器中的指针元素换为shared_ptr时,即vector<shared_ptr< int>>,当vector生命期结束时,析构容器中的元素,而shared_ptr对象可以自动释放其管理的内存资源,就不会发生内存泄露。
    (3)管理动态数组。

    4、weak_ptr

    weak_ptr也是基类_Ptr_base的派生类,是用户生成弱共享智能指针的接口。weak_ptr的定义比shared_ptr简单很多。

    weak_ptr的构造函数、赋值函数与shared_ptr类似,但是只能通过其它weak_ptr、shared_ptr来构造,在此不再重复。

    weak_ptr中没有重载->操作符,无法获取资源指针,无法访问资源的内存空间,只能判断资源是否有效;

    weak_ptr对象与shared_ptr对象共享一个计数器对象,计数器对象在构造第一个shared_ptr对象时生成;

    weak_ptr利用expired函数检查资源是否有效,若返回true(即引用计数为0),表示资源已经释放,否则,至少有一个shared_ptr对象持有资源。

    weak_ptr不能直接访问资源,而必须调用lock函数检查资源是否被释放,并返回一个shared_ptr对象。若资源已被释放(即expired为true),返回的是一个空shared_ptr对象;否则,返回一个shared_tr,引用计数+1。

    同shared_ptr一样,weak_ptr的生命期结束时,会自动调用析构函数,析构函数中会调用计数器的_Decwref()函数,资源的弱引用计数-1,若弱引用计数为0,就会调用_Delete_this() 函数删除计数器对象。

    综上所述,智能指针weak_ptr的原理为:weak_ptr对象依赖shared_ptr/weak_ptr对象生成,并自动调用计数器递增弱引用计数。当某个weak_ptr生命期结束时,会自动调用析构函数,析构函数中会通过计数器递减弱引用计数,若弱引用计数为0,析构函数就会调用计数器的_Delete_this()函数删除计数器对象。

    不知道大家有没有注意到一个问题,为什么在计数器初始化的时候就要将弱引用计数设置为1呢?
    若弱引用计数初始化为0,在强引用计数不为0的情况下,weak_ptr对象生命期都结束时,此时弱引用计数为0,就会删除计数器,但这时share_ptr对象尚在使用、资源也未释放,就会出现内存错误。

    5、shared_ptr使用注意事项

    在使用shared_ptr指针的过程中,一定要注意避免智能指针的循环引用从而导致的内存泄露,而解决这个问题的方法就是在可能出现循环引用的地方使用weak_ptr来替代shared_ptr。
    举例说明:(以下内容来自博客:关于shared_ptr与weak_ptr的使用。)

    #include <iostream>
    #include <boost/smart_ptr.hpp>
    using namespace std;
    using namespace boost;
    
    class BB;
    class AA
    {
    public:
        AA() { cout << "AA::AA() called" << endl; }
        ~AA() { cout << "AA::~AA() called" << endl; }
        shared_ptr<BB> m_bb_ptr;  //!
    };
    
    class BB
    {
    public:
        BB() { cout << "BB::BB() called" << endl; }
        ~BB() { cout << "BB::~BB() called" << endl; }
        shared_ptr<AA> m_aa_ptr; //!
    };
    
    int main()
    {
        shared_ptr<AA> ptr_a (new AA);
        shared_ptr<BB> ptr_b ( new BB);
        cout << "ptr_a use_count: " << ptr_a.use_count() << endl;
        cout << "ptr_b use_count: " << ptr_b.use_count() << endl;
        //下面两句导致了AA与BB的循环引用,结果就是AA和BB对象都不会析构
        ptr_a->m_bb_ptr = ptr_b;
        ptr_b->m_aa_ptr = ptr_a;
        cout << "ptr_a use_count: " << ptr_a.use_count() << endl;
        cout << "ptr_b use_count: " << ptr_b.use_count() << endl;
    }
    

    运行结果:
    在这里插入图片描述
    由结果可知:由于类A和B中内部的成员变量shared_ptr各自保存了对方的一次引用,使得shared_ptr对象ptr_a和ptr_b各自的引用计数均为2,程序结束时两者的引用计数均-1但都不为0,因此不会释放各自持有的内存资源,即A和B的析构函数不会被调用,因此就发生了内存泄露。

    出现循环引用的解决办法就是将其中一个shared_ptr改为weak_ptr,对应代码如下:

    #include <iostream>
    #include <boost/smart_ptr.hpp>
    using namespace std;
    using namespace boost;
    
    class BB;
    class AA
    {
    public:
        AA() { cout << "AA::AA() called" << endl; }
        ~AA() { cout << "AA::~AA() called" << endl; }
        weak_ptr<BB> m_bb_ptr;  //!
    };
    
    class BB
    {
    public:
        BB() { cout << "BB::BB() called" << endl; }
        ~BB() { cout << "BB::~BB() called" << endl; }
        shared_ptr<AA> m_aa_ptr; //!
    };
    
    int main()
    {
        shared_ptr<AA> ptr_a (new AA);
        shared_ptr<BB> ptr_b ( new BB);
        cout << "ptr_a use_count: " << ptr_a.use_count() << endl;
        cout << "ptr_b use_count: " << ptr_b.use_count() << endl;
        //下面两句导致了AA与BB的循环引用,结果就是AA和BB对象都不会析构
        ptr_a->m_bb_ptr = ptr_b;
        ptr_b->m_aa_ptr = ptr_a;
        cout << "ptr_a use_count: " << ptr_a.use_count() << endl;
        cout << "ptr_b use_count: " << ptr_b.use_count() << endl;
    }
    

    运行结果:
    在这里插入图片描述
    从结果可以看到ptr_a的引用计数为2,而ptr_b的引用计数为1,这是因为weak_ptr不会改变强引用计数,因此在程序结束时ptr_a与ptr_b的引用计数均会-1,此时ptr_b的引用计数变为0,就会释放其持有的内存资源即BB发生析构,而BB析构时又会释放其成员变量m_aa_ptr就会使ptr_a的引用计数-1变为0,从而ptr_a就会释放其持有的资源,即AA发生析构。由此可见,此时不会发生内存泄露。

    参考博客:share_ptr与weak_ptr的区别与联系

    展开全文
  • new、delete初识(new、delete) new 、delete 申请的内存是在堆里面,和c语言中的malloc、free一样。是直接内存管理方式。 class A { // 称为间接内存管理 int i, j; // 临时对象 ...int *ptr2 = n
  • 因为动态内存的使用我们很容易忘记释放内存,容易造成内存的...weak_ptr:指向shared_ptr所管理的对象,防止循环引用等。 智能指针的用法和普通指针没有什么区别: 1.shared_ptr 初始化方式: **初始化为指针为空。*
  • 智能指针(三):weak_ptr浅析

    万次阅读 多人点赞 2018-09-01 14:36:26
    weak_ptr这个指针天生一副“小弟”的模样,也是在C++11的时候引入的标准库,它的出现完全是为了弥补它老大shared_ptr天生有缺陷的问题,其实相比于上一代的智能指针auto_ptr来说,新进老大shared_ptr可以说近乎完美...
  • 弱类型指针weak_ptr的使用(详解)

    千次阅读 2021-01-23 16:34:50
    弱类型指针weak_ptr的使用 由shared_ptr不正当使用引发的错误来引出weak_ptr 我们看结合代码运行结果仔细观察如下程序: #include<iostream> usingnamespacestd; #include<memory> classB; ...
  • shared_ptr shared_ptr 是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象,定义在 memory 文件中,命名空间为 std。shared_ptr最初实现于Boost库中,后由 C++11 引入到 C++ STL。shared_ptr 利用引用...
  • std::unique_ptr是一种独占的语义,即只允许一个智能指针引用裸指针,这区别于std::shared_ptr允许多个shared_ptr引用同一个裸指针,它没有引用计数,它的性能比shared_ptr会高一点。 在用法上std::unique_ptr和std...
  •   C++里面的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是C++11支持,并且第一个已经被11弃用。 为什么要用智能指针   智能指针的作用是管理一个指针,因为存在以下这种情况:申请的...
  • C++11中weak_ptr的使用

    2020-05-12 17:14:08
    weak_ptr提供了expired()与lock()成员函数,前者用于判断weak_ptr指向的对象是否已被销毁,后者返回其所指对象的shared_ptr智能指针(对象销毁时返回”空”shared_ptr)。 智能指针是模板类而不是指针. weak_ptr并没有...
  • std::shared_ptr std::shared_ptr是通过指针保持对象共享所有权的智能指针,多个std::shared_ptr引用对象占用同一个对象。当指针对象被另一个对象引用时可以把所有权共享给另一个引用指针对象,被管理的指针是当...
  • C++11智能指针(unique_ptr、shared_ptrweak_ptr)

    千次阅读 多人点赞 2019-07-11 23:31:15
    更多文章欢迎访问 程序员小非 博客 很多人怕写C/C++ 程序就是因为指针,因为指针给了程序员高度的自由,同样也赋予了高度的责任,稍有不慎就...C++ 11中定义了unique_ptr、shared_ptrweak_ptr三种智能指针(smart ...
  • weak_ptr也是一个引用计数型的智能指针,但是它不增加对象的引用计数,即弱(weak)引用。 Shared_ptr在下列情况之一出现时销毁对象并释放内存: 最后占有std::shared_ptr对象被销毁时; 最后占有std::shared_ptr...
  • 虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案,如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的
  • 1. auto_ptr auto_ptr主要是用来解决资源自动释放的问题,比如如下代码: void Function() { Obj*p = new Obj(20); ... if (error occor) throw ... 或者 retrun; delete p; } 在函数遇到错误之后,一般会抛异常...
  • 头文件<memory> 1、std::unique_ptr 声明: template<class T,class Deleter = std::default_delete<... class unique_ptr;... std::unique_ptr是通过指针占有并管理另一对象,并在unique_ptr离开作
  • 分析了智能指针weak_ptr源码,并借此完成了自己的weak_ptr版本,需要注意的是对于weak_ptr由于其是shared_ptr的伴随指针,故处处离不开shared_ptr,故代码逻辑很像shared_ptr,写出来的显得有点多。
  • weakptr的作为弱引用指针,其实现依赖于counter的计数器类和share_ptr的赋值,构造,所以先把counter和share_ptr简单实现 ...counter对象的目地就是用来申请一个块内存来存引用基数,s是share_ptr的引用计数,w是weak_
  • weak_ptr源码分析

    2020-08-09 11:05:56
    在经历过shared_ptr源码分析之后,我们接下来继续分析weak_ptrweak_ptr是作为shared_ptr的补充而出现的,用于避免循环引用问题。 weak_ptr结构与shared_ptr近乎一致,这里直接写出部分结构和关键函数。 weak_ptr&...
  • c++最开始设计没有考虑gc(garbage collection)机制,后续c++11推出后STL标准库中增加了三个智能指针类,...weak_ptr主要就是为了配合shared_ptr防止出现循环引用(两个对象各持有各自的shared_ptr, 都无法先释放), ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,359
精华内容 6,543
关键字:

Weak_ptr