Smart Pointers源码 + CppCon笔记Back to Basics: Smart Pointers

智能指针是基于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;
}

析构函数

扫描二维码关注公众号,回复: 11448438 查看本文章
~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更准确),且多个shared_ptr实例共用一个__shared_count(__shared_count中赋值构造函数可以看到_Sp_counted_base<_Lp>* __tmp = __r._M_pi;)。不过切忌分别创建两个独立的shared_ptr指向同个资源,control block(计数模块)是不一样的,还是会double delete。

成员变量

_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_base喜加一
void
_M_add_ref_copy()
{ __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }

// __shared_count的复制 -> 使用同一个_Sp_counted_base,并让其喜加一
__shared_count(const __shared_count& __r) noexcept
: _M_pi(__r._M_pi)
{
  if (_M_pi != 0)
    _M_pi->_M_add_ref_copy();
}

_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

weak_ptr不能解引用,所以可以理解成一个shared_ptr的门票,如果你由一个weak_ptr你也就能获得相应的shared_ptr(使用门票来使用)。

3.1.weak_ptr使用

3.1.1.weak_ptr由shared_ptr获得

shared_ptr<Thing> sp(new Thing);
weak_ptr<Thing> wp1(sp); // construct wp1 from a shared_ptr
weak_ptr<Thing> wp2; // an empty weak_ptr - points to nothing

wp2 = sp; // wp2 now points to the new Thing
weak_ptr<Thing> wp3 (wp2); // construct wp3 from a weak_ptr
weak_ptr<Thing> wp4
wp4 = wp2; // wp4 now points to the new Thing.

3.1.2.weak_ptr转为shared_ptr

使用lock成员函数

shared_ptr<_Tp>
lock() const noexcept
{ return shared_ptr<_Tp>(*this, std::nothrow); } 

std::nothrow是一个用于区分重载的东西,区别是如果weak_ptr已经expired了,会抛出异常。

shared_ptr有对应的构造函数(throwing)

// throwing版本
// Now that __weak_count is defined we can define this constructor:
template<_Lock_policy _Lp>
inline
__shared_count<_Lp>::__shared_count(const __weak_count<_Lp>& __r)
: _M_pi(__r._M_pi)
{
  if (_M_pi != nullptr)
    _M_pi->_M_add_ref_lock();
  else
    __throw_bad_weak_ptr(); // 如果weak_ptr已经expired了,抛出一个bad weak ptr异常
}

// no throwing版本
// Now that __weak_count is defined we can define this constructor:
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;
}

推荐使用lock,再判读是否是空指针(是否expired)

3.1.3.expired和use_count

判断weak_ptr所指对象是否expired,和多少shared_ptr对象指向被管理的资源对象。

bool expired() const noexcept
{ return _M_refcount._M_get_use_count() == 0; }

long use_count() const noexcept
{ return _M_refcount._M_get_use_count(); }

3.1.3.std::enable_shared_from_this

还有一个问题没有解决,当一个对象T创建后,如果一个函数需要shared_ptr 的形参, 我们该如何解决?显然用shared_ptr是不合理的,因为这样离开函数体时候对象T就会被释放,很容易引起问题。此时我们需要有个办法,让对象T自己能拿出一个shared_ptr ,且shared_ptr的control block都相同,不会使用完函数时候把T释放。这时候就可以使用基于CRTP技法的std::enable_shared_from_this,让对象自己拥有一张门票(weak_ptr),每次只需要share_from_this()换成shared_ptr 就好了。(weak_from_this() is in C++17)

同样,这也是一种让函数介入对象生命周期的方法。


/**
*  @brief Base class allowing use of member function shared_from_this.
*/
template<typename _Tp>
class enable_shared_from_this {
      /*省略*/
public:
      mutable weak_ptr<_Tp>  _M_weak_this;

      shared_ptr<_Tp> shared_from_this()
      { return shared_ptr<_Tp>(this->_M_weak_this); }
}



猜你喜欢

转载自www.cnblogs.com/linsinan1995/p/13382719.html