shared_ptr源码分析

本文详细探讨了C++中智能指针shared_ptr的工作原理。包括其内部引用计数器的实现细节,不同线程同步策略的选择,以及如何通过模板特化来适应不同的应用场景。此外,还介绍了shared_ptr的标准定义及其API特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

// 引用计数器的同步方式, 单线程情形无需同步, 多线程下如支持原子操作则使用atomic, 否则退化为锁
enum _Lock_policy { _S_single, _S_mutex, _S_atomic };

static const _Lock_policy __default_lock_policy =
#ifndef __GTHREADS
    _S_single;
#elif defined _GLIBCXX_HAVE_ATOMIC_LOCK_POLICY
    _S_atomic;
#else
    _S_mutex;
#endif

// _Lock_policy枚举将用来模板特化具体的_Mutex_base
// TODO 为什么atomic不需要使用barrier?
template <_Lock_policy _Lp>
class _Mutex_base {
 protected:
  // The atomic policy uses fully-fenced builtins, single doesn't care.
  // TODO
  enum { _S_need_barriers = 0 };
};

// TODO
// template <>
// class _Mutex_base<_S_mutex> : public __gnu_cxx::__mutex {
//  protected:
//   enum { _S_need_barriers = 1 };
// };

// 引用计数器的父类
// 这个基类的设计非常巧妙, 其目的是区分两种实现子类, 主要是区分由指针构造shared_ptr
// 和直接make_shared的实现, 直接make_shared将对象和control block一次性分配在同一块内存中,
// 而由构造器产生的shared_ptr只需要维护一个指针即可
// 同时这种封装将子类异构的模板封装了起来, 阻止了模板化扩散, 简化和解耦了程序结构, 对于__shared_count是隐式的
// 代价是虚函数的成本
template <_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp> {
 public:
  // TODO 为什么_M_weak_count要设置为1?
  _Sp_counted_base() noexcept : _M_use_count(1), _M_weak_count(1) {}

  virtual ~_Sp_counted_base() noexcept {}

  // 这里居然是直接返回void* (TODO 怎么保证类型安全?)
  virtual void* _M_get_deleter(const std::type_info&) noexcept = 0;

  // 每次拷贝shared_ptr, _M_use_count+1
  // 注意这里形成了acq-rel顺序 (TODO 为什么不是relax?)
  void _M_add_ref_copy() { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }

  // 注意这两个操作区分开来了, 其目的应该是所有的shared_ptr释放后,
  // 应当立即释放维护的对象, 但不释放计数器本身, 当所有的weak_ptr释放后,
  // 再释放计数器本身
  virtual void _M_dispose() noexcept = 0;
  virtual void _M_destroy() noexcept { delete this; }

  // TODO
  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();
      }
    }
  }

  // 返回引用计数, 原子计数
  // 注意这里提到的`no synchronization with other threads`
  // 首先relaxed会产生synchronization吗, 不会, 不仅不构成happens-before关系, 也不构成synchronization-with关系
  // 这里不使用barrier是有意为之, TODO
  // decrementing the shared_ptr counters requires acquire -
  // release synchronization with the destructor
  long _M_get_use_count() const noexcept {
    // No memory barrier is used here so there is no synchronization
    // with other threads.
    return __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED);
  }

 private:
  // 引用计数
  _Atomic_word _M_use_count;
};

// 引用计数器
template <_Lock_policy _Lp>
class __shared_count {
 private:
  _Sp_counted_base<_Lp>* _M_pi;
};

// shared_ptr会直接public继承__shared_ptr
// 单独提出一个__shared_ptr的目的是封装LP这个模板参数, 其中LP会传递给__shared_count
// __shared_ptr持有一个元素指针和引用计数器
template <typename _Tp, _Lock_policy _Lp = __default_lock_policy>
class __shared_ptr {
 public:
  // (1) T=>T (2) T[]=>T
  using element_type = typename remove_extent<_Tp>::type;

 private:
  // 元素指针
  // 注意类型不是T, 是element_type, 为了支持数组
  element_type* ptr;
  // 引用计数器, 也就是所谓的control block
  __shared_count<_Lp> _M_refcount;
};

// 摘自标准提案对于shared_ptr的定义和api标准, 对于理解shared_ptr非常重要

// A shared_ptr can share ownership of an object while storing a pointer to another object. This feature can be used to
// point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), the
// dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count reaches
// zero.

// A shared_ptr may also own no objects, in which case it is called empty (an empty shared_ptr may have a non-null
// stored pointer if the aliasing constructor was used to create it).

// All specializations of shared_ptr meet the requirements of CopyConstructible, CopyAssignable, and LessThanComparable
// and are contextually convertible to bool.

// All member functions (including copy constructor and copy assignment) can be called by multiple threads on different
// instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of
// the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those
// accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of
// atomic functions can be used to prevent the data race.
template <typename T>
class shared_ptr {
  // Constructs a shared_ptr with no managed object, i.e. empty shared_ptr
  // constexpr shared_ptr() noexcept;
  // constexpr shared_ptr( std::nullptr_t ) noexcept;

  // Constructs a shared_ptr with ptr as the pointer to the managed object.
  // Y* must be convertible to T*.
  // template< class Y >
  // explicit shared_ptr( Y* ptr );

  // Constructs a shared_ptr with ptr as the pointer to the managed object.
  // Uses the specified deleter d as the deleter. The expression d(ptr) must be well formed, have well-defined behavior
  // and not throw any exceptions. The construction of d and of the stored deleter from d must not throw exceptions.
  // These constructors additionally do not participate in overload resolution if the expression d(ptr) is not
  // well-formed, or if std::is_move_constructible<D>::value is false.
  // template< class Y, class Deleter >
  // shared_ptr( Y* ptr, Deleter d );

  // Constructs a shared_ptr which shares ownership of the object managed by r. If r manages no object, *this manages no
  // object too. The template overload doesn't participate in overload resolution if Y* is not compatible with T*
  // shared_ptr( const shared_ptr& r ) noexcept;
  // template< class Y >
  // shared_ptr( const shared_ptr<Y>& r ) noexcept;

  // Move-constructs a shared_ptr from r. After the construction, *this contains a copy of the previous state of r, r is
  // empty and its stored pointer is null. The template overload doesn't participate in overload resolution if Y* is not
  // compatible with (since C++17) T*.
  // shared_ptr( shared_ptr&& r ) noexcept;
  // template< class Y >
  // shared_ptr(shared_ptr<Y>&& r ) noexcept;

  // operator= assigns the shared_ptr
  // reset replaces the managed object
  // swap swaps the managed objects
  // get returns the stored pointer
  // operator*
  // operator-> dereferences the stored pointer
  // use_count returns the number of shared_ptr objects referring to the same managed object
  // operator bool checks if the stored pointer is not null

 private:
};

#include <memory>

int main() {
  std::shared_ptr<int> p();
  //
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值