C++深入剖析智能指针
本文深入shared_ptr,weak_ptr以及unique_ptr的MSVC源码实现,理解智能指针的具体实现
文章目录
- shared_ptr
- weak_ptr
- unique_ptr
shared_ptr
首先在MSVC里shared_ptr继承自_Ptr_base
_EXPORT_STD template <class _Ty>
class shared_ptr : public _Ptr_base<_Ty>
默认构造函数和传入nullptr的构造函数也没做什么
constexpr shared_ptr() noexcept = default;
constexpr shared_ptr(nullptr_t) noexcept {} // construct empty shared_ptr
主要看传入非空指针的构造函数
template <class _Ux,enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,_SP_convertible<_Ux, _Ty>>,int> = 0>explicit shared_ptr(_Ux* _Px) { // construct shared_ptr object that owns _Pxif constexpr (is_array_v<_Ty>) {_Setpd(_Px, default_delete<_Ux[]>{});} else {_Temporary_owner<_Ux> _Owner(_Px);_Set_ptr_rep_and_enable_shared(_Owner._Ptr, new _Ref_count<_Ux>(_Owner._Ptr));_Owner._Ptr = nullptr;}}
首先是条件编译,总结就是指针是可delete且_Ux可转换为_Ty,才实例化该模板:
-
enable_if_t<...>=0是一个用于SFINAE(替换失败并不是一个错误)的工具。它用于根据某些编译时逻辑有条件地启用或禁用模板实例化。如果enable_if_t中的条件为真,则启用此模板;否则,他将从候选重载集合中移除。 -
conjunction_v<...>执行逻辑与操作,都为真才为真 -
conditional_t<...>是一个编译时的if语句,它根据作为第一个参数指定的条件,在第二个或第三个模板参数之间进行选择。
explicit 表示其是显式的构造函数
if constexpr 也是用于条件编译,如果_Ty是一个数组,那么编译上面的代码,否则编译下面的代码。
这里我们关注于不是数组的情况,_Temporary_owner 是临时保存原始指针,具体就是封装了一层
template <class _Ux>
struct _Temporary_owner {_Ux* _Ptr;explicit _Temporary_owner(_Ux* const _Ptr_) noexcept : _Ptr(_Ptr_) {}_Temporary_owner(const _Temporary_owner&) = delete;_Temporary_owner& operator=(const _Temporary_owner&) = delete;~_Temporary_owner() {delete _Ptr;}
};
_Set_ptr_rep_and_enable_shared 首先看一下 new _Ref_count<_Ux>(_Owner._Ptr)
template <class _Ty>
class _Ref_count : public _Ref_count_base { // handle reference counting for pointer without deleter
public:explicit _Ref_count(_Ty* _Px) : _Ref_count_base(), _Ptr(_Px) {}private:void _Destroy() noexcept override { // destroy managed resourcedelete _Ptr;}void _Delete_this() noexcept override { // destroy selfdelete this;}_Ty* _Ptr;
};
_Ref_count 继承自 _Ref_count_base 其持有原始指针的拷贝,以及重写了 _Destroy 和 _Delete_this
进一步看下 _Ref_count_base,两个私有变量,初始都是 1,一个用于使用计数,一个用于weak_ptr计数
_Atomic_counter_t _Uses = 1;
_Atomic_counter_t _Weaks = 1;
接下来继续看下 _Set_ptr_rep_and_enable_shared,主要就是把指针和引用计数赋给当前 shared_ptr
template <class _Ux>void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Pxthis->_Ptr = _Px;this->_Rep = _Rx;if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) {if (_Px && _Px->_Wptr.expired()) {_Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));}}}
下面看下传入一个共享指针的拷贝构造函数
shared_ptr(const shared_ptr& _Other) noexcept { // construct shared_ptr object that owns same resource as _Otherthis->_Copy_construct_from(_Other);}
_Copy_construct_from 是基类 _Ptr_base 的方法,顺便贴下移动构造函数
template <class _Ty2>void _Move_construct_from(_Ptr_base<_Ty2>&& _Right) noexcept {// implement shared_ptr's (converting) move ctor and weak_ptr's move ctor_Ptr = _Right._Ptr;_Rep = _Right._Rep;_Right._Ptr = nullptr;_Right._Rep = nullptr;}template <class _Ty2>void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {// implement shared_ptr's (converting) copy ctor_Other._Incref();_Ptr = _Other._Ptr;_Rep = _Other._Rep;}
看下 _Other._Incref,就是调用计数模块的 _Incref 方法
void _Incref() const noexcept {if (_Rep) {_Rep->_Incref();}}
_MT_INCR 是宏定义,展开后可以看出是内部加锁的递增,保证多线程下的安全性
void _Incref() noexcept { // increment use count_MT_INCR(_Uses);// 展开宏定义后// _InterlockedIncrement(reinterpret_cast<volatile long*>(&_Uses));
}
最后看下析构函数,调用基类的 _Decref 方法
~shared_ptr() noexcept { // release resourcethis->_Decref();}
基类的 _Decref 方法就是调用引用对象的_Decref方法
void _Decref() noexcept { // decrement reference countif (_Rep) {_Rep->_Decref();}}
引用对象的_Decref方法就是一个内部加锁的减减操作,如果减完是0的话,释放掉资源
void _Decref() noexcept { // decrement use countif (_MT_DECR(_Uses) == 0) {_Destroy();_Decwref();}}
_Decwref 减少weak引用计数,如果等于0了,_Delete_this 把 Ref_count 对象释放掉,上面的_Destroy是吧持有的原始指针对象释放掉
void _Decwref() noexcept { // decrement weak reference countif (_MT_DECR(_Weaks) == 0) {_Delete_this();}}
我们也可以自定义一个 deletor 传入,得是一个可调用对象
template <class _Ux, class _Dx,enable_if_t<conjunction_v<is_move_constructible<_Dx>, _Can_call_function_object<_Dx&, _Ux*&>,_SP_convertible<_Ux, _Ty>>,int> = 0>shared_ptr(_Ux* _Px, _Dx _Dt) { // construct with _Px, deleter_Setpd(_Px, _STD move(_Dt));}
_Setpd 里new了一个 _Ref_count_resource 其和 _Ref_count 都是继承自 _Ref_count_base
template <class _UxptrOrNullptr, class _Dx>void _Setpd(const _UxptrOrNullptr _Px, _Dx _Dt) { // take ownership of _Px, deleter _Dt_Temporary_owner_del<_UxptrOrNullptr, _Dx> _Owner(_Px, _Dt);_Set_ptr_rep_and_enable_shared(_Owner._Ptr, new _Ref_count_resource<_UxptrOrNullptr, _Dx>(_Owner._Ptr, _STD move(_Dt)));_Owner._Call_deleter = false;}
主要是 Destroy 虚函数与 _Ref_count 实现的不同,其保存了一个 _Compressed_pair<_Dx, _Resource> 而不仅仅是一个原始指针
private:void _Destroy() noexcept override { // destroy managed resource_Mypair._Get_first()(_Mypair._Myval2);}void _Delete_this() noexcept override { // destroy selfdelete this;}_Compressed_pair<_Dx, _Resource> _Mypair;
那么再 _Destroy 时,就是把 deletor 取出来,然后以原始指针为参数传入调用
weak_ptr
与 shared_ptr 一样,weak_ptr 也是继承自 _Ptr_base
_EXPORT_STD template <class _Ty>
class weak_ptr : public _Ptr_base<_Ty>
看下拷贝构造函数,都是调用基类的 _Weakly_construct_from
weak_ptr(const weak_ptr& _Other) noexcept {this->_Weakly_construct_from(_Other); // same type, no conversion}template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept {this->_Weakly_construct_from(_Other); // shared_ptr keeps resource alive during conversion}
根据 _Other.Rep 是否为空执行不同分支,如果非空,当前持有相同的原始指针和引用对象,且增加 引用对象的weak引用计数
template <class _Ty2>void _Weakly_construct_from(const _Ptr_base<_Ty2>& _Other) noexcept { // implement weak_ptr's ctorsif (_Other._Rep) {_Ptr = _Other._Ptr;_Rep = _Other._Rep;_Rep->_Incwref();} else {_STL_INTERNAL_CHECK(!_Ptr && !_Rep);}}
再看一下 weak_ptr 常用的几个方法的实现
use_count获取原始指针的引用计数,非weak引用计数
// inline long std::_Ptr_base<_Ty>::use_count() const_NODISCARD long use_count() const noexcept {return _Rep ? _Rep->_Use_count() : 0;}//inline long std::_Ref_count_base::_Use_count() constlong _Use_count() const noexcept {return static_cast<long>(_Uses);}
本质就是返回引用对象里的 _Uses 值,所以当一个Weak_Ptr 指向的共享指针生命周期都结束了,use_count 就为 0 了。
expired也是同理,只是一个_Uses值是否为 0 的判断
_NODISCARD bool expired() const noexcept {return this->use_count() == 0;}
lock获取共享指针
_NODISCARD shared_ptr<_Ty> lock() const noexcept { // convert to shared_ptrshared_ptr<_Ty> _Ret;(void) _Ret._Construct_from_weak(*this);return _Ret;}
定义一个空构造的共享指针,然后调用基类的 _Construct_from_weak
template <class _Ty2>bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()if (_Other._Rep && _Other._Rep->_Incref_nz()) {_Ptr = _Other._Ptr;_Rep = _Other._Rep;return true;}return false;}
引用对象的指针非空,且对其引用计数增加的结果不为0,就把对应的原始指针和引用对象指针传给共享智能指针。_Incref_nz 的实现也是考虑到线程安全的,使用CAS来避免竞争,这里的CAS就是比较交换原子操作,比较成功这里是 _Volatile_uses 和 _Count 的比较则将 _Count+1 写入目标位置,否则不写入,最后都要返回 _Volatile_uses 原本的值。如果_Count!=0则返回true,否则返回false(比如最后一个共享指针释放先执行了,那么此时_Volatile_uses=0,那么比较失败返回了0,原本_Count=1,此时while循环中止,返回false)。
bool _Incref_nz() noexcept { // increment use count if not zero, return true if successfulauto& _Volatile_uses = reinterpret_cast<volatile long&>(_Uses);
#ifdef _M_CEE_PURElong _Count = *_Atomic_address_as<const long>(&_Volatile_uses);
#elselong _Count = __iso_volatile_load32(reinterpret_cast<volatile int*>(&_Volatile_uses));
#endifwhile (_Count != 0) {const long _Old_value = _INTRIN_RELAXED(_InterlockedCompareExchange)(&_Volatile_uses, _Count + 1, _Count);if (_Old_value == _Count) {return true;}_Count = _Old_value;}return false;}
最后看下 weak_ptr 的析构函数,就是调用引用对象的 _Decwref 方法
~weak_ptr() noexcept {this->_Decwref();}
unique_ptr
unique_ptr 独占所有权,其不需要引用对象,所以没有继承 _Ptr_base,此外
_EXPORT_STD template <class _Ty, class _Dx /* = default_delete<_Ty> */>
class unique_ptr { // non-copyable pointer to an object
其拷贝构造函数和拷贝赋值运算符都是被删除的
unique_ptr(const unique_ptr&) = delete;unique_ptr& operator=(const unique_ptr&) = delete;
有默认deletor和指定deletor两种构造函数
template <class _Dx2 = _Dx, _Unique_ptr_enable_default_t<_Dx2> = 0>_CONSTEXPR23 explicit unique_ptr(pointer _Ptr) noexcept : _Mypair(_Zero_then_variadic_args_t{}, _Ptr) {}template <class _Dx2 = _Dx, enable_if_t<is_constructible_v<_Dx2, const _Dx2&>, int> = 0>_CONSTEXPR23 unique_ptr(pointer _Ptr, const _Dx& _Dt) noexcept : _Mypair(_One_then_variadic_args_t{}, _Dt, _Ptr) {}
主要看下移动构造函数
template <class _Dx2 = _Dx, enable_if_t<is_move_constructible_v<_Dx2>, int> = 0>_CONSTEXPR23 unique_ptr(unique_ptr&& _Right) noexcept: _Mypair(_One_then_variadic_args_t{}, _STD forward<_Dx>(_Right.get_deleter()), _Right.release()) {}
_Right.release() ,就是和空进行交换,并返回原有的指针
_CONSTEXPR23 pointer release() noexcept {return _STD exchange(_Mypair._Myval2, nullptr);}
最后看下析构函数,如果原始指针非空,deletor释放资源
_CONSTEXPR23 ~unique_ptr() noexcept {if (_Mypair._Myval2) {_Mypair._Get_first()(_Mypair._Myval2);}}
