C++深入剖析智能指针(更新ing)

发布于:2024-08-25 ⋅ 阅读:(228) ⋅ 点赞:(0)

本文深入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_thisRef_count 对象释放掉,上面的_Destroy是吧持有的原始指针对象释放掉

    void _Decwref() noexcept { // decrement weak reference count
        if (_MT_DECR(_Weaks) == 0) {
            _Delete_this();
        }
    }