本文深入shared_ptr,weak_ptr以及unique_ptr的MSVC源码实现,理解智能指针的具体实现
文章目录
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 _Px
if 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 resource
delete _Ptr;
}
void _Delete_this() noexcept override { // destroy self
delete 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 _Px
this->_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 _Other
this->_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 resource
this->_Decref();
}
基类的 _Decref
方法就是调用引用对象的_Decref
方法
void _Decref() noexcept { // decrement reference count
if (_Rep) {
_Rep->_Decref();
}
}
引用对象的_Decref
方法就是一个内部加锁的减减操作,如果减完是0的话,释放掉资源
void _Decref() noexcept { // decrement use count
if (_MT_DECR(_Uses) == 0) {
_Destroy();
_Decwref();
}
}
_Decwref
减少weak引用计数,如果等于0了,_Delete_this
把 Ref_count
对象释放掉,上面的_Destroy
是吧持有的原始指针对象释放掉
void _Decwref() noexcept { // decrement weak reference count
if (_MT_DECR(_Weaks) == 0) {
_Delete_this();
}
}