How shared_ptr(new T()) differs from make_shared()
函数 make_shared() 位于文件 shared_ptr.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/** * @brief Create an object that is owned by a shared_ptr. * @param __args Arguments for the @a _Tp object's constructor. * @return A shared_ptr that owns the newly created object. * @throw std::bad_alloc, or an exception thrown from the * constructor of @a _Tp. */ template<typename _Tp, typename... _Args> inline shared_ptr<_Tp> make_shared(_Args&&... __args) { typedeftypename std::remove_const<_Tp>::type _Tp_nc; return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), std::forward<_Args>(__args)...); }
函数内部直接将操作转交给了 std::allocate_shared。这个函数虽然是 C++ 11 引入的,但是似乎是从 boost 沿袭而来,我们在 MSVC STL 的分析里并未见到过。
注:reference 上对这个函数的描述简洁明了,点名了就是用来实现一次内存分配的。事实上截止到 Visual Studi 2017 15.6.3,这个函数还只在 TR1 名字空间里。
/** * @brief Create an object that is owned by a shared_ptr. * @param __a An allocator. * @param __args Arguments for the @a _Tp object's constructor. * @return A shared_ptr that owns the newly created object. * @throw An exception thrown from @a _Alloc::allocate or from the * constructor of @a _Tp. * * A copy of @a __a will be used to allocate memory for the shared_ptr * and the new object. */ template<typename _Tp, typename _Alloc, typename... _Args> inline shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&... __args) { returnshared_ptr<_Tp>(_Sp_make_shared_tag(), __a, std::forward<_Args>(__args)...); }
// This constructor is non-standard, it is used by allocate_shared. template<typename _Alloc, typename... _Args> shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a, _Args&&... __args) : __shared_ptr<_Tp>(__tag, __a, std::forward<_Args>(__args)...) {}
// This constructor is non-standard, it is used by allocate_shared. template<typename _Alloc, typename... _Args> __shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a, _Args&&... __args) : _M_ptr(), _M_refcount(__tag, (_Tp*)0, __a, std::forward<_Args>(__args)...) { // _M_ptr needs to point to the newly constructed object. // This relies on _Sp_counted_ptr_inplace::_M_get_deleter. void* __p = _M_refcount._M_get_deleter(typeid(__tag)); _M_ptr = static_cast<_Tp*>(__p); __enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr); }
// _M_ptr needs to point to the newly constructed object. // This relies on _Sp_counted_ptr_inplace::_M_get_deleter. void* __p = _M_refcount._M_get_deleter(typeid(__tag)); _M_ptr = static_cast<_Tp*>(__p);
// class _Sp_counted_ptr_inplace // Sneaky trick so __shared_ptr can get the managed pointer virtualvoid* _M_get_deleter(const std::type_info& __ti) noexcept { #if __cpp_rtti if (__ti == typeid(_Sp_make_shared_tag)) returnconst_cast<typename remove_cv<_Tp>::type*>(_M_ptr()); #endif returnnullptr; }
/** * @brief Construct a %shared_ptr that owns the pointer @a __p. * @param __p A pointer that is convertible to element_type*. * @post use_count() == 1 && get() == __p * @throw std::bad_alloc, in which case @c delete @a __p is called. */ template<typename _Tp1> explicitshared_ptr(_Tp1* __p) : __shared_ptr<_Tp>(__p) {}
/** * @brief Base class allowing use of member function shared_from_this. */ template<typename _Tp> classenable_shared_from_this { protected: constexprenable_shared_from_this()noexcept{}
template<> inlinevoid _Sp_counted_base<_S_atomic>:: _M_add_ref_lock() { // Perform lock-free add-if-not-zero operation. _Atomic_word __count = _M_get_use_count(); do { if (__count == 0) __throw_bad_weak_ptr(); // Replace the current counter value with the old value + 1, as // long as it's not changed meanwhile. } while (!__atomic_compare_exchange_n(&_M_use_count, &__count, __count + 1, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)); }
template<> inlinebool _Sp_counted_base<_S_atomic>:: _M_add_ref_lock_nothrow() { // Perform lock-free add-if-not-zero operation. _Atomic_word __count = _M_get_use_count(); do { if (__count == 0) returnfalse; // Replace the current counter value with the old value + 1, as // long as it's not changed meanwhile. } while (!__atomic_compare_exchange_n(&_M_use_count, &__count, __count + 1, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)); returntrue; }
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_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(); } }
// from weak_ptr shared_ptr<_Tp> lock()constnoexcept { returnshared_ptr<_Tp>(*this, std::nothrow); }
// from shared_ptr // This constructor is non-standard, it is used by weak_ptr::lock(). shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t) : __shared_ptr<_Tp>(__r, std::nothrow) { }
// from __shared_ptr // This constructor is used by __weak_ptr::lock() and // shared_ptr::shared_ptr(const weak_ptr&, std::nothrow_t). __shared_ptr(const __weak_ptr<_Tp, _Lp>& __r, std::nothrow_t) : _M_refcount(__r._M_refcount, std::nothrow) { _M_ptr = _M_refcount._M_get_use_count() ? __r._M_ptr : nullptr; }
// 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; }
可以看出这里和 MSVC STL 大致上还是类似的。
利用 CAS 去加 usage-count,如果成功了,说明对象还活着,成功 promotion;反之,计数为0,自增函数返回 false,构造一个空对象。