ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Smart Pointers源码解读

2020-07-27 03:00:25  阅读:546  来源: 互联网

标签:__ count Pointers Sp counted 源码 shared ptr Smart


智能指针是基于RAII的理念设计的一个资源的封装,能让类不直接管理资源,从而减少错误发生(忘记释放)。

1.unique_ptr

智能指针的理解要和资源的所有权相联系。unique_ptr代表的是独占的所有权(exclusive ownership),所封装的指针不能与其他共享,否则double free。

1.1.unique_ptr实现

unique_ptr主要的组成是一个所管理的指针,一个可以自定义的deleter, 而且move only(毕竟不能共享指针)。是一个处理low-level和非RAII的好方法。

原理:

unique_ptr的声明

template <typename _Tp, typename _Tp_Deleter = default_delete<_Tp> > 
class unique_ptr;

unique_ptr中禁止拷贝

// Disable copy from lvalue.
unique_ptr(const unique_ptr&) = delete;
template<typename _Up, typename _Up_Deleter> 
unique_ptr(const unique_ptr<_Up, _Up_Deleter>&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
template<typename _Up, typename _Up_Deleter> 
unique_ptr& operator=(const unique_ptr<_Up, _Up_Deleter>&) = delete;

deleter

template<typename _Tp> 
struct default_delete<_Tp>
{
    default_delete() {}

    template<typename _Up>
    default_delete(const default_delete<_Up>&) { }
    
    void operator()(_Tp* __ptr) const {
        static_assert(sizeof(_Tp)>0, "can't delete pointer to incomplete type");
        delete __ptr;
    } 
};

此外还有一套针对array的定义,这里使用了template的specialization技法,输入的是array的话会调用这个类。https://stackoverflow.com/questions/19923353/multiple-typename-arguments-in-c-template。

template<typename _Tp, typename _Tp_Deleter> 
class unique_ptr<_Tp[], _Tp_Deleter>

移动构造,release能获得unique_ptr所管理指针,并释放所有权

// Move constructors.
unique_ptr(unique_ptr&& __u) 
: _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }

__tuple_type _M -> typedef std::tuple<_Tp*, _Tp_Deleter> __tuple_type;

pointer get() const
{ return std::get<0>(_M_t); }

typename std::add_lvalue_reference<deleter_type>::type
get_deleter()
{ return std::get<1>(_M_t); }

pointer release() 
{
    pointer __p = get();
    std::get<0>(_M_t) = 0; // nullify 所管理指针
    return __p;
}

析构函数

~unique_ptr() { reset(); }

// 替换所管理的指针,如果没有参数,就是与一个空指针替换
reset(pointer __p = pointer()) {
    if (__p != get()) {        // unique_ptr为空时不会释放
         get_deleter()(get()); // 等同deleter(ptr), 使用deleter释放ptr
         std::get<0>(_M_t) = __p;
    }
}

1.2.make_unique实现

unique_ptr dptr = make_unique (1);

forward + variadic template(并不是pack expression,所以c++14就有make_unique)

/// std::make_unique for single objects
template<typename _Tp, typename... _Args>
inline typename _MakeUniq<_Tp>::__single_object
make_unique(_Args&&... __args)
{ return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }

1.3.unique_ptr使用

所有权转换的例子

// a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
    p->bar();
    return p;
}

int main() {
    auto p = std::make_unique<D>(); // p is a unique_ptr that owns a D
    auto q = pass_through(std::move(p)); 
    assert(!p); // now p owns nothing and holds a null pointer
    q->bar();   // and q owns the D object
}

1.3.1.自定义deleter

2.shared_ptr

一种管理可共享资源的智能指针,这就是说有多个shared_ptr指向同一个指针,并存在一个计数,当计数为0时后才释放资源。

2.0.shared_ptr和weak_ptr

解决成环问题所以引入了weak_ptr,只增加weak_count不增加use_count
https://stackoverflow.com/questions/4984381/shared-ptr-and-weak-ptr-differences

class A { shared_ptr<B> b; ... };
class B { shared_ptr<A> a; ... };
shared_ptr<A> x(new A);  // +1
x->b = new B;            
x->b->a = x;             // +1
// Ref count of 'x' is 2.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, there will be a memory leak:
// 2 is decremented to 1, and so both ref counts will be 1.
// (Memory is deallocated only when ref count drops to 0)

2.1.shared_ptr实现

原理

shared_ptr包含了资源指针和一个计数的内部类,在计数类__shared_count中,包含保存引用计数,弱引用计数还有一个指向deleter和资源指针的_Sp_counted_base。_Sp_counted_ptr是对_Sp_counted_base的封装。

2.1.1.shared_ptr和__shared_ptr

shared_ptr只是__shared_ptr的简单封装,而__shared_ptr包含了资源指针和一个计数类__shared_count。 __shared_count负责资源计数,释放并包含deleter。

// __shared_ptr的成员
element_type*           _M_ptr;         // Contained pointer.
__shared_count<_Lp>  _M_refcount;       // Reference counter.拥有deleter和资源指针

shared_ptr只是__shared_ptr的简单封装

// Construct a shared_ptr that owns the pointer __p.

template<typename _Tp1>
explicit shared_ptr(_Tp1* __p)
  : __shared_ptr<_Tp>(__p) { }

__shared_ptr包含资源的指针和一个栈上的计数对象__shared_count,__shared_ptr不负责_M_ptr的释放,这些活是由__shared_count来负责的。

// __shared_ptr的声明
template<typename _Tp, _Lock_policy _Lp>
    class __shared_ptr
    : public __shared_ptr_access<_Tp, _Lp> {/* 省略*/}

// __shared_ptr 的成员变量
element_type*           _M_ptr;         // Contained pointer.
__shared_count<_Lp>     _M_refcount;    // Reference counter.拥有deleter和资源指针

// __shared_ptr 的析构函数
~__shared_ptr() = default;

__shared_ptr的构造函数之一

template<typename _Yp, typename _Deleter, typename = _SafeConv<_Yp>>
__shared_ptr(_Yp* __p, _Deleter __d)
    : _M_ptr(__p), _M_refcount(__p, std::move(__d)) // _M_refcount拥有deleter和资源指针
{
  static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
      "deleter expression d(p) is well-formed");
  _M_enable_shared_from_this_with(__p);
}

2.1.2.__shared_ptr_access

__shared_ptr的父类__shared_ptr_access主要负责重载解引用和->操作符。

using element_type = _Tp;
element_type&
operator*() const noexcept
{
    __glibcxx_assert(_M_get() != nullptr); // 会对是否为空指针进行判断。
    return *_M_get();
}

2.1.3.__shared_count

__shared_count是shared_ptr的计数模块,也是真正管理释放的部分(或者说_Sp_counted_base更准确)。用有成员变量

_Sp_counted_base<_Lp>*  _M_pi;

__shared_count的构造函数

template<_Lock_policy _Lp>
class __shared_count{/* 省略*/}

// _Sp_counted_base<_Lp>*  _M_pi;
template<typename _Ptr>
explicit __shared_count(_Ptr __p) : _M_pi(0) {
    __try {
        // _Sp_counted_base<_Lp>* _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
        _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p); 
    }
    __catch(...) {
        delete __p;
        __throw_exception_again;
    }
}

__shared_count的析构函数

~__shared_count() noexcept
{
  if (_M_pi != nullptr)
    _M_pi->_M_release(); // use_count--, 如果use_count等于1,清理资源。
}

针对_M_pi_, 其可能是Sp_counted_deleter或者_Sp_counted_ptr的多态表现。

Sp_counted_deleter在_Sp_counted_ptr的基础外多了deleter和allocator

// 自定义deleter时的__shared_count的构造函数
// ::new (__mem) _Sp_cd_type(__p, std::move(__d), std::move(__a)); // placement new, 在已分配的内存__mem上再分配
// _Sp_counted_base<_Lp>*  _M_pi = __mem;

template<typename _Ptr, typename _Deleter, typename _Alloc>
__shared_count(_Ptr __p, _Deleter __d, _Alloc __a) : _M_pi(0)
{
  typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type;
  __try
    {
      typename _Sp_cd_type::__allocator_type __a2(__a);
      auto __guard = std::__allocate_guarded(__a2);
      _Sp_cd_type* __mem = __guard.get();
      ::new (__mem) _Sp_cd_type(__p, std::move(__d), std::move(__a));
      _M_pi = __mem;
      __guard = nullptr;
    }
  __catch(...)
    {
      __d(__p); // Call _Deleter on __p.
      __throw_exception_again;
    }
}

2.1.4._Sp_counted_base

_Sp_counted_base的成员函数

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

_Sp_counted_base的构造函数和析构函数

template<_Lock_policy _Lp = __default_lock_policy>
    class _Sp_counted_base
    : public _Mutex_base<_Lp> {/* 省略*/}

// _Sp_counted_base的构造函数
_Sp_counted_base() noexcept
      : _M_use_count(1), _M_weak_count(1) { }

// _Sp_counted_base的析构函数
virtual
~_Sp_counted_base() noexcept
{ }

_Sp_counted_base的资源释放函数

// Called when _M_use_count drops to zero, to release the resources
// managed by *this.
virtual void
_M_dispose() noexcept = 0;

 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)
        {
          __atomic_thread_fence (__ATOMIC_ACQ_REL);
        }
      // 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();
        }
    }
}

_Sp_counted_ptr 和 _Sp_counted_deleter

// Counted ptr with no deleter or allocator support
  template<typename _Ptr, _Lock_policy _Lp>
    class _Sp_counted_ptr final : public _Sp_counted_base<_Lp> {/* 省略*/}

// _Sp_counted_ptr的释放函数
virtual void
_M_dispose() noexcept
{ delete _M_ptr; }

virtual void
_M_destroy() noexcept
{ delete this; }

// Support for custom deleter and/or allocator
  template<typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp>
    class _Sp_counted_deleter final : public _Sp_counted_base<_Lp> {/* 省略*/}

_Sp_counted_deleter的释放
// _Impl类是利用EBO的一种实现,没看明白

~_Sp_counted_deleter() noexcept { }

virtual void
_M_dispose() noexcept
{ _M_impl._M_del()(_M_impl._M_ptr); }

virtual void
_M_destroy() noexcept
{
  __allocator_type __a(_M_impl._M_alloc());
  __allocated_ptr<__allocator_type> __guard_ptr{ __a, this };
  this->~_Sp_counted_deleter();
}

2.2.Aliasing constructor

// Aliasing constructor
     /**
 *  @brief  Constructs a %shared_ptr instance that stores @a __p
 *          and shares ownership with @a __r.
 *  @param  __r  A %shared_ptr.
 *  @param  __p  A pointer that will remain valid while @a *__r is valid.
 *  @post   get() == __p && use_count() == __r.use_count()
 *
 *  This can be used to construct a @c shared_ptr to a sub-object
 *  of an object managed by an existing @c shared_ptr.
 *
 * @code
 * shared_ptr< pair<int,int> > pii(new pair<int,int>());
 * shared_ptr<int> pi(pii, &pii->first);
 * assert(pii.use_count() == 2);
 * @endcode
 */
template<typename _Yp>
  shared_ptr(const shared_ptr<_Yp>& __r, element_type* __p) noexcept
  : __shared_ptr<_Tp>(__r, __p) { }

// template<typename _Tp, _Lock_policy _Lp = __default_lock_policy>  class __shared_ptr;

template<typename _Yp>
__shared_ptr(const __shared_ptr<_Yp, _Lp>& __r,
      element_type* __p) noexcept
      _M_ptr(__p), _M_refcount(__r._M_refcount) // never throws
}

一个shared_ptr的特殊用法,指向类的成员,并增加类的引用计数,来增加使用成员类时的生命周期

struct Bar { 
    // some data that we want to point to
};

struct Foo {
    Bar bar;
};

shared_ptr<Foo> f = make_shared<Foo>(some, args, here);
shared_ptr<Bar> specific_data(f, &f->bar);

// ref count of the object pointed to by f is 2
f.reset();

// the Foo still exists (ref cnt == 1)
// so our Bar pointer is still valid, and we can use it for stuff
some_func_that_takes_bar(specific_data);

2.3.进行使用make_shared

  • new和delete对应
  • cache locality

用同个malloc申请内存给control block(计数模块)和资源 -> 在相邻地址

3.weak_ptr

标签:__,count,Pointers,Sp,counted,源码,shared,ptr,Smart
来源: https://www.cnblogs.com/linsinan1995/p/13382719.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有