当前位置: 首页 > news >正文

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_thisRef_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 常用的几个方法的实现

  1. 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 了。

  1. expired 也是同理,只是一个 _Uses 值是否为 0 的判断
    _NODISCARD bool expired() const noexcept {return this->use_count() == 0;}
  1. 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);}}

http://www.mrgr.cn/news/9090.html

相关文章:

  • 【PyTorch】关于Transforms的简单使用
  • 解决MySQL的PacketTooBigException异常问题
  • 服务端事件(Server-Sent Events):实现实时Web通信的利器
  • 探索Qotom Q51251OPS迷你电脑:功能与广泛应用
  • Angular中的依赖注入是如何工作的?它的优势是什么?
  • C语言:深入理解文件操作
  • Redis的持久化介绍及其Linux配置介绍
  • List<对象>转JSON字符串以及JSON字符串转List<对象>
  • 大模型重塑就医体验:医联MedGPT助力健康中国建设
  • k8s搭建
  • vagrant 创建虚拟机
  • k8s中service对象
  • HTTP协议
  • 【Linux】如何快速查看 linux 服务器有几个cpu
  • 网站上线流程完全手册:域名、服务器与CDN
  • Java并发编程的核心概念--线程与进程
  • 河南萌新2024第五场
  • LeetCode100.删除链表的倒数第 N 个结点
  • Button窗口部件
  • ARM——操作示例