注:源码来源为 llvm/llvm-project
使用智能指针时我们所包含的头文件为<memory>
,而关于shared_ptr<T>
的类定义实际上在shared_ptr.h
中,为了便于理解,此处仅从源码中截取出其部分构造函数、成员变量以及重载的赋值运算符:
template<class _Tp>
class shared_ptr
{
private:
element_type* __ptr_; // 所管理的对象内存的指针
__shared_weak_count* __cntrl_; // 引用计数相关的指针
......
shared_ptr() _NOEXCEPT : __ptr_(nullptr), __cntrl_(nullptr) { }
explicit shared_ptr(_Yp* __p) : __ptr_(__p) { // 传入堆内存指针的构造函数
unique_ptr<_Yp> __hold(__p); // 此处使用 unique_ptr 是和目的?
typedef typename __shared_ptr_default_allocator<_Yp>::type _AllocT;
typedef __shared_ptr_pointer<_Yp*, __shared_ptr_default_delete<_Tp, _Yp>, _AllocT > _CntrlBlk;
__cntrl_ = new _CntrlBlk(__p, __shared_ptr_default_delete<_Tp, _Yp>(), _AllocT());
__hold.release();
__enable_weak_this(__p, __p);
}
shared_ptr(const shared_ptr& __r) _NOEXCEPT // 复制构造函数
: __ptr_(__r.__ptr_), __cntrl_(__r.__cntrl_)
{
if (__cntrl_)
__cntrl_->__add_shared(); // 增加引用计数
}
shared_ptr(shared_ptr&& __r) _NOEXCEPT // 移动构造函数
: __ptr_(__r.__ptr_), __cntrl_(__r.__cntrl_)
{
__r.__ptr_ = nullptr;
__r.__cntrl_ = nullptr;
}
~shared_ptr() // 析构函数
{
if (__cntrl_)
__cntrl_->__release_shared(); // 释放内存
}
......
shared_ptr<_Tp>& operator=(const shared_ptr& __r) _NOEXCEPT // 赋值运算
{
shared_ptr(__r).swap(*this);
return *this;
}
......
};
从传入堆内存指针的构造函数不难看出,与引用计数相关的对象指针__cntrl_
在此处通过new申请堆内存来保存,而将一个share_ptr
对象复制给另一个share_ptr
对象时,除了直接对其__ptr_
和__cntrl_
赋值,还会执行__add_shared()
方法,从方法的名字就能看出这里边一定是增加了引用计数。
与引用计数相关的类__shared_weak_count
是__shared_count
的子类,__shared_weak_count
的__add_shared()
方法实际上就是调用__shared_count
的__add_shared()
:
void __add_shared() _NOEXCEPT {
__shared_count::__add_shared();
}
该方法的底层实际上是使用原子操作来增加计数值:
void __shared_count::__add_shared() noexcept
{
__libcpp_atomic_refcount_increment(__shared_owners_); // ↓见下方函数
}
template <class _Tp>
inline _Tp __libcpp_atomic_refcount_increment(_Tp& __t) _NOEXCEPT
{
#if defined(_LIBCPP_HAS_BUILTIN_ATOMIC_SUPPORT) && !defined(_LIBCPP_HAS_NO_THREADS)
return __atomic_add_fetch(&__t, 1, __ATOMIC_RELAXED); // 原子操作 +1
#else
return __t += 1;
#endif
}
而在重载的赋值运算符函数中,则是通过shared_ptr(__r).swap(*this);
的方法来进行复制操作,这里实际上利用了复制构造函数将引用计数加1,然后再通过swap()
完成赋值。