赞
踩
// 引用计数器的同步方式, 单线程情形无需同步, 多线程下如支持原子操作则使用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(); // }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。