unique-ptr源码解析

2023-10-27


title: unique_ptr源码解析
date: 2022-09-22 21:00:56
tags:
- Modern C++
- C++
- C++ Library

前言

这篇博客是对unique_ptr源代码的分析,本文使用的编译器是MinGW

本篇文章不保证能够说明清楚unique_ptr实现的所有细节以及原因,但会尽可能的做到这样

需要读者有TMP的基础,例如熟悉std::enable_if, std::remove_referene等等的元函数

本篇文章不是一个unique_ptr的使用教程,但是会提及到一些使用中的细节

术语约定

  • 对内置指针有时候称其为raw-pointer/裸指针
  • 对于unique_ptr的指向堆区对象的指针,有时候称其为内部指针,有时候称其为指向堆区的指针,或者其他称呼。请联系上下文判别于内置指针的区别。
  • 对于类型需要满足的条件,我们称其为约束条件

描述约定

  • 对于大部分的篇幅,都会使用源代码+注释的方式来描述。如果一个问题使用注释描述篇幅过大,会单独拿出来说明
  • 对于所有的源码注释,均保留

unique_ptr概述

unique_ptr是比rew-pointer更为好的选择,标准库实现unique_ptr使接口尽可能的接近原生指针。不同的编译器对其实现有所不同。

gcc编译器对 unique_ptr的实现,大概分为以下几部分

  • defaule_delete 定义的默认删除器,其中没有任何数据成员,是unique_ptr的默认删除器
  • __uniq_ptr_impl unique_ptr的核心部分
  • __uniq_ptr_data unique_ptr 和 __uniq_ptr_impl的中间层,unique_ptr内含一个__uniq_ptr_data
  • unique_ptr 非数组版本,也是最常用的版本
  • unique_ptr<T[]> 数组版本,一般情况下有更好的替代(vector,array等)
  • std::make_unique,unique_ptr的配套函数,用于创建unique_ptr

需要了解的前置知识

类型安全

C++支持OOP,这就涉及到的多态,本章的主题是指针,如果想放心的对于unique_ptr的使用就像使用内置指针一样(某些部分),需要做类型安全的约束。包括但不限于

  • 基类和派生类的指针转换
  • 数组和指针的转换
  • 删除器的转换(读者看到删除器的实现就会知道了)

为什么需要额外的类型安全,而不是靠编译器的检擦?

因为C++是一个弱类型语言,就是说在不同的类型之间可以实现隐式的类型转换——这种转换有时候很便利。但有更多的时候会产生副作用。例如精度丢失,数据溢出,甚至是内存泄漏和未定义行为。

区别T[]和T*

在C中T[]T*可以看作是等价的类型,但是在C++的模板中,请将T[]T*看作不同类型
例如std::is_array的实现

template<typename>
    struct is_array
    : public false_type { };

  template<typename _Tp, std::size_t _Size>
    struct is_array<_Tp[_Size]>
    : public true_type { };

  template<typename _Tp>
    struct is_array<_Tp[]>
    : public true_type { };

类似的如果想用unique_ptr存放数组unique_ptr<T[]>是正确的,而unique_ptr<T*>是不正确的

实现

gcc对于unique_ptr的实现放在了unique_ptr.h中

本篇文章将按照源文件从上至下的顺序来剖析unique_ptr的实现

unique_ptr定义的默认删除器

//基本的默认删除器实现
/// Primary template of default_delete, used by unique_ptr for single objects
  template <typename _Tp> struct default_delete {
    /// Default constructor
    constexpr default_delete() noexcept = default;

    /** @brief Converting constructor.
     *
     * Allows conversion from a deleter for objects of another type, `_Up`,
     * only if `_Up*` is convertible to `_Tp*`.
     */
     // 正如注释所说,如果底层指针允许准换,则允许删除器转换
     // 空实现目的是做类型约束
     // 使用_Requires检查_Up * 能否转换为_TP *
     // 例如能用基类指针来析构一个派生类对像,却不能用派生类指针析构基类对象(先通过语法再说)
    template <typename _Up, typename = _Require<is_convertible<_Up *, _Tp *>>>
    default_delete(const default_delete<_Up> &) noexcept {}

    //重载的调用运算符,接受一个_Tp指针,对其进行delete
    /// Calls `delete __ptr`
    void operator()(_Tp *__ptr) const {
        //如果是一个非完整类型,例如只声明没有定义,会断言失败
      static_assert(!is_void<_Tp>::value,
                    "can't delete pointer to incomplete type");
      static_assert(sizeof(_Tp) > 0, "can't delete pointer to incomplete type");
      delete __ptr;
    }
  };
// _GLIBCXX_RESOLVE_LIB_DEFECTS
  // DR 740 - omit specialization for array objects with a compile time length

  /// Specialization of default_delete for arrays, used by `unique_ptr<T[]>`
  template <typename _Tp> struct default_delete<_Tp[]> {
  public:
    /// Default constructor
    constexpr default_delete() noexcept = default;

    /** @brief Converting constructor.
     *
     * Allows conversion from a deleter for arrays of another type, such as
     * a const-qualified version of `_Tp`.
     *
     * Conversions from types derived from `_Tp` are not allowed because
     * it is undefined to `delete[]` an array of derived types through a
     * pointer to the base type.
     */
     //允许删除其转换为另一种类型的数组,例如转换为_TP的const限定版本
     //但是不允许从子类转换为父类,不要用多态的方式处理数组,通过基类指针删除由派生类的数组是未定义的
     //所以禁止这种转换
     //具体的信息请参考《More Effective C++》条款5
    template <typename _Up,
              typename = _Require<is_convertible<_Up (*)[], _Tp (*)[]>>>
    default_delete(const default_delete<_Up[]> &) noexcept {}

    /// Calls `delete[] __ptr`
    template <typename _Up>
    typename enable_if<is_convertible<_Up (*)[], _Tp (*)[]>::value>::type //注意这里的类型是数组的指针
                                    //虽然允许派生类指针隐式的转换为基类指针,为了检查这种转换,我们要写成这样
                                    //例如可以从non-const 转换为const,但不能从derived ** 转换为 base **
    operator()(_Up *__ptr) const {
      static_assert(sizeof(_Tp) > 0, "can't delete pointer to incomplete type");
      //调用delete[]
      delete[] __ptr;
    }
  };

__uniq_ptr_impl是实现部分,把这个类读懂,就读懂了unique_ptr的一半。unique_ptr的核心实现,全部在这个类中。没有复杂的逻辑,但是做到类型安全的实现方法,还是比较难的部分。__uniq_ptr_impl中没有复杂的关于类型安全的约束。比较复杂的约束在uniqe_ptr<T>unique_ptr<T[]>

// Manages the pointer and deleter of a unique_ptr
//_TP是指向的类型, _DP是删除器的类型
  template <typename _Tp, typename _Dp> class __uniq_ptr_impl {
    
    //这里做了一个type_traits
    //_Up实际上是_Tp, _Ep实际上是_Dp
    //指向堆区对象指针的的类型为_Up *
    template <typename _Up, typename _Ep, typename = void> struct _Ptr {
      using type = _Up *;
    };

    //如果删除器中声明了删除的指针类型,有优先考虑这个指针类型
    //如果没有,类型替换失败,转而匹配上面的_Ptr
    //这样做的原因,想想一种情况
    //class B; class D : public B;
    //删除器中接受B*而实际存储的对象为D类型对象,在删除器中定义了pointer为B*
    template <typename _Up, typename _Ep>
    struct _Ptr<_Up, _Ep,
                __void_t<typename remove_reference<_Ep>::type::pointer>> {
      //这里假定了给定的_Ep有type类型,并且type下有pointer类型
      using type = typename remove_reference<_Ep>::type::pointer;
    };

  public:
    //这里做了一个约束条件
    //只是简单的别名定义,std::enable_if::没有取type
    //用到的地方再unique_ptr中
    //这个模板别名目的是为了约束_Dp也就是删除器,不能是一个指针,并且要求具有默认构造函数
    using _DeleterConstraint = enable_if<
        __and_<__not_<is_pointer<_Dp>>, is_default_constructible<_Dp>>::value>;

    //pointer为_Tp, _Ep经过 _Ptr类型萃取后的的别名
    //::type萃取出来一个指针类型
    //我们把pointer 这个类型直接当作raw-pointer类型
    using pointer = typename _Ptr<_Tp, _Dp>::type;

    //正如断言所言,unique_ptr的删除其必须是一个函数对象或者一个左值引用
    //如果是一个右值引用, !value == false,断言失败
    static_assert(!is_rvalue_reference<_Dp>::value,
                  "unique_ptr's deleter type must be a function object type"
                  " or an lvalue reference type");

    __uniq_ptr_impl() = default; // =defalut是调用tuple的默认构造,tuple中的默认构造会为每个对象给定一个初值
                                 //例如int等类型会被初始化为0,而指针类型会被初始化为nullptr
    __uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }

    //通过裸指针+删除器的方式构造
    //删除器使用万能引用+转发的形式传递
    template <typename _Del>
    __uniq_ptr_impl(pointer __p, _Del &&__d)
        : _M_t(__p, std::forward<_Del>(__d)) {}

    //移动构造
    //注意如果类型没有移动构造函数情况就是拷贝构造
    __uniq_ptr_impl(__uniq_ptr_impl &&__u) noexcept
    //将u下的_M_t直接移动给自己
        : _M_t(std::move(__u._M_t)) {
      __u._M_ptr() = nullptr; //滞空u下的_M_ptr
    }

    //移动赋值运算符
    __uniq_ptr_impl &operator=(__uniq_ptr_impl &&__u) noexcept {
      reset(__u.release());
      _M_deleter() = std::forward<_Dp>(__u._M_deleter()); //将删除器转发过去 为什么用转发??
      return *this;
    }

    //_M_ptr获得指向堆区内存的指针
    pointer &_M_ptr() { return std::get<0>(_M_t); }
    //对于const的版本,我们按值返回即可,因为按值返回pointer的耗费较小
    pointer _M_ptr() const { return std::get<0>(_M_t); }
    //_M_ptr获得删除器
    _Dp &_M_deleter() { return std::get<1>(_M_t); }
    //对于删除其,使用const &方式返回,因为删除器是可能是函数对象——这种情值返回可能耗费较大
    //并且删除器有可能不支持拷贝操作
    const _Dp &_M_deleter() const { return std::get<1>(_M_t); }

    //reset调用删除器析构raw-pointer指向的空间,并重新赋值为__p
    //假定不会抛出异常 使用noexcept
    void reset(pointer __p) noexcept {
      const pointer __old_p = _M_ptr();
      _M_ptr() = __p;
      //不为nullptr, 进行析构操作
      if (__old_p)
        _M_deleter()(__old_p);
    }

    //并不析构指针指向的内存,而是返回指针
    //并且将指向堆区部分的指针赋值为nullptr
    pointer release() noexcept {
      pointer __p = _M_ptr();
      _M_ptr() = nullptr;
      return __p;
    }

    //对于两个unique_ptr的swap,只需要交换其指针部分和删除器部分即可
    void swap(__uniq_ptr_impl &__rhs) noexcept {
      //这里的using std::swap请参考《Effective C++》条款25
      using std::swap;
      swap(this->_M_ptr(), __rhs._M_ptr());
      swap(this->_M_deleter(), __rhs._M_deleter());
    }

  private:
    //使用一个tuple来存储具体指向对象的指针和删除器,便于实现,上面默认构造函数中有解释
    //并且,tuple具有空间压缩的功能(模板递归继承)
    //这种方式为什么能够压缩空间,是另一个问题了,参考《深度探索C++对象模型》
    tuple<pointer, _Dp> _M_t;
  };

关于删除器的约束

我们可以测试一下。正如我们所说在默认构造一个智能指针的情况下,不允许是一个函数指针,

这是由于内置指针在初始化的时候,也就是删除器初始化的时候,是一个nullptr。所以,在默认构造的情况下,不允许删除器是一个指针类型

如果把指针类型的删除器(函数指针)当作类型参数,编译器会说我找不到默认的构造函数,这是因为unique_ptr的默认构造函数进行了如此的约束,类型替换失败,于是找不到默认构造函数。如果析构器是函数对象或者lambda,就不会有这样的情况

namespace jan {
    template<typename _Dp>
    using _DeleterConstraint = enable_if<
            __and_<__not_<is_pointer<_Dp>>, is_default_constructible<_Dp>>::value>;
}
void fun() { }
struct Foo {
    Foo() = delete;
};
int main() {
    typename jan::_DeleterConstraint<decltype(fun)>::type; //error
    typename jan::_DeleterConstraint<int>::type;    //ok
    typename jan::_DeleterConstraint<Foo>::type;    //error
    return 0;
}

关于_Ptr萃取指针的说明

从源代码中可以看出,如果删除器中定义了一个pointer型别,_Ptr会有限萃取出删除器::pointer作为unique_ptr的raw-pointer类型。我们想象这样一种情况。

class A { };
class B : public A { };
class C : public B { };

struct BDel {
    using pointer = B*;
    void operator()(pointer p) {
        delete p;
    }
};
struct ADel{
//    using pointer = B*;
    void operator()(A* p) {
        //do something
        //但是和BDel做的事情不一样
        delete p;
    }
};

int main() {
    unique_ptr<C,BDel> p (new C, BDel{});
    return 0;
}

指定Bel为删除器类型,就表明,我们要处理的类型/要删除的类型,是B类型或者B的派生类。记者OOP中的一条规则吗?面向接口编程,而不是面向实例编程。这这个情况中,尽管是个C对象,但是仍当作B类型处理。如果没有_Ptr萃取,unique_ptr::pointer就是C*,无法应用于B对象。

就是说尽管我们这样写unique_ptr<C,BDel> p (new C, BDel{});,仍是可以应用于B对象,就像这样unique_ptr<C,BDel> p (new B, BDel{})

是有一些莫名其妙!!!

__uniq_ptr_data

在__uniq_ptr_impl和unique_ptr,又加上了一个中间层,目的是根据删除器是否有移动构造或者移动赋值属性,
部分特例化,来匹配不同的情况

// Defines move construction + assignment as either defaulted or deleted.
  template <typename _Tp, typename _Dp,
            bool = is_move_constructible<_Dp>::value,
            bool = is_move_assignable<_Dp>::value>
  struct __uniq_ptr_data : __uniq_ptr_impl<_Tp, _Dp> {
    //uisng 使用__uniq_ptr_impl的构造函数,下同
    using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
    __uniq_ptr_data(__uniq_ptr_data &&) = default;
    __uniq_ptr_data &operator=(__uniq_ptr_data &&) = default;
  };

  template <typename _Tp, typename _Dp>
  struct __uniq_ptr_data<_Tp, _Dp, true, false> : __uniq_ptr_impl<_Tp, _Dp> {
    using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
    __uniq_ptr_data(__uniq_ptr_data &&) = default;
    __uniq_ptr_data &operator=(__uniq_ptr_data &&) = delete;
  };

  template <typename _Tp, typename _Dp>
  struct __uniq_ptr_data<_Tp, _Dp, false, true> : __uniq_ptr_impl<_Tp, _Dp> {
    using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
    __uniq_ptr_data(__uniq_ptr_data &&) = delete;
    __uniq_ptr_data &operator=(__uniq_ptr_data &&) = default;
  };

  template <typename _Tp, typename _Dp>
  struct __uniq_ptr_data<_Tp, _Dp, false, false> : __uniq_ptr_impl<_Tp, _Dp> {
    using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
    __uniq_ptr_data(__uniq_ptr_data &&) = delete;
    __uniq_ptr_data &operator=(__uniq_ptr_data &&) = delete;
  };

千呼万唤始出来,犹抱琵琶半遮面

/// 20.7.1.2 unique_ptr for single objects.
  template <typename _Tp, typename _Dp = default_delete<_Tp>> class unique_ptr {
    template <typename _Up>
    //如果不符合 __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint中的约束条件
    //enable_if<false>不会取到type这个类型,类型替换失败
    using _DeleterConstraint =
        typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;

    //内涵一个_uniq_ptr_data来实现unique_ptr
    __uniq_ptr_data<_Tp, _Dp> _M_t;

  public:
    //简单的型别别名
    using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer;
    using element_type = _Tp;   //被指向元素类型
    using deleter_type = _Dp;   //删除器类型

  private:
    // helper template for detecting a safe conversion from another unique_ptr
    //帮助模板推到是否是安全的转换从另一个unique_ptr
    template <typename _Up, typename _Ep>
    using __safe_conversion_up =
        //先推导两个unique_ptr的内部指针转换是否安全,再看_Up是否是一个数组(数组可以退化为指针,这样的转换是不安全的)
        //约束_Up不能是一个数组
        __and_<is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
               __not_<is_array<_Up>>>;

  public:
    //关于unique_ptr的方法功能,可以看见已经有很好的说明了,所以这里我不再说明
    // Constructors.

    /// Default constructor, creates a unique_ptr that owns nothing.
    //模板约束再这里使用,因为这里的删除器约束条件是impl中的约束条件::type
    //相当于对std::enable_if取type
    //如果删除器是一个指针类型,或者没有默认构造函数,会编译失败
    template <typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
    constexpr unique_ptr() noexcept : _M_t() {}


    /** Takes ownership of a pointer.
     *
     * @param __p  A pointer to an object of @c element_type
     *
     * The deleter will be value-initialized.
     */
     //同上
    template <typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
    explicit unique_ptr(pointer __p) noexcept : _M_t(__p) {}

    /** Takes ownership of a pointer.
     *
     * @param __p  A pointer to an object of @c element_type
     * @param __d  A reference to a deleter.
     *
     * The deleter will be initialized with @p __d
     */
     //这里做的删除器类型约束为:需要能拷贝构造
    template <typename _Del = deleter_type,
              typename = _Require<is_copy_constructible<_Del>>>
    unique_ptr(pointer __p, const deleter_type &__d) noexcept
        : _M_t(__p, __d) {}

    /** Takes ownership of a pointer.
     *
     * @param __p  A pointer to an object of @c element_type
     * @param __d  An rvalue reference to a (non-reference) deleter.
     *
     * The deleter will be initialized with @p std::move(__d)
     */
     //这里做的删除器类型约束为:需要能拷贝构造
    template <typename _Del = deleter_type,
              typename = _Require<is_move_constructible<_Del>>>
    unique_ptr(
        pointer __p,
        //约束为不能是一个左值引用
        __enable_if_t<!is_lvalue_reference<_Del>::value, _Del &&> __d) noexcept
        : _M_t(__p, std::move(__d)) {}

    //删除器类型是左值引用,并且对于传入的删除器是一个右值,是不允许的所以  =delete
    template <typename _Del = deleter_type,
              typename _DelUnref = typename remove_reference<_Del>::type>
    unique_ptr(pointer,
               __enable_if_t<is_lvalue_reference<_Del>::value, _DelUnref &&>) =
        delete;

    //对于nullptr,也就是std::nullptr_t类型的构造函数例如unique_ptr<Foo> p(nullptr);
    /// Creates a unique_ptr that owns nothing.
    template <typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
    constexpr unique_ptr(nullptr_t) noexcept : _M_t() {}


    /// Move constructor.
    //仅仅是=default
    unique_ptr(unique_ptr &&) = default;

    /** @brief Converting constructor from another type
     *
     * Requires that the pointer owned by @p __u is convertible to the
     * type of pointer owned by this object, @p __u does not own an array,
     * and @p __u has a compatible deleter type.
     */
     //正如注释所言,从一个unique_ptr构造一个unique_ptr,要求拥有的指针能够转换并且不是数组类型
     //并且要求__u的删除器能够兼容this的删除器
    template <
        typename _Up, typename _Ep,
        typename = _Require<
            __safe_conversion_up<_Up, _Ep>, //看看内部指针能不能安全转换
            typename conditional<is_reference<_Dp>::value, 
                                 is_same<_Ep, _Dp>, //看看删除器是不是一个引用类型,是,需要相同类型
                                 is_convertible<_Ep, _Dp>>::type>> //不是需要_Ep(__u的删除器类型)能够转换为_Dp
    unique_ptr(unique_ptr<_Up, _Ep> &&__u) noexcept
        : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) {}

#if _GLIBCXX_USE_DEPRECATED
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    /// Converting constructor from @c auto_ptr
    //因为auto_ptr是废弃的特性,所以这个函数不说明了,有兴趣的话读者可以自己分析一下
    template <typename _Up,
              typename = _Require<is_convertible<_Up *, _Tp *>,
                                  is_same<_Dp, default_delete<_Tp>>>>
    unique_ptr(auto_ptr<_Up> &&__u) noexcept;
#pragma GCC diagnostic pop
#endif

    /// Destructor, invokes the deleter if the stored pointer is not null.
    ~unique_ptr() noexcept {
      //如果删除器不是可调用的,会断言失败
      //__is_invocable具体的实现,这里不说明
      static_assert(__is_invocable<deleter_type &, pointer>::value,
                    "unique_ptr's deleter must be invocable with a pointer");
      auto &__ptr = _M_t._M_ptr(); //先缓存指针
      if (__ptr != nullptr)        //如果不是空,使用删除器删除
        get_deleter()(std::move(__ptr)); 
      __ptr = pointer();   
    }

    // Assignment.

    /** @brief Move assignment operator.
     *
     * Invokes the deleter if this object owns a pointer.
     */
     //同样的,简单是使用default
    unique_ptr &operator=(unique_ptr &&) = default;

    /** @brief Assignment from another type.
     *
     * @param __u  The object to transfer ownership from, which owns a
     *             convertible pointer to a non-array object.
     *
     * Invokes the deleter if this object owns a pointer.
     */
     //涉及到类型的转换,这个问题上面已经看到过了
     //可以类似的如法炮制
    template <typename _Up, typename _Ep>
    typename enable_if<__and_<__safe_conversion_up<_Up, _Ep>,
                              is_assignable<deleter_type &, _Ep &&>>::value,
                       unique_ptr &>::type
    operator=(unique_ptr<_Up, _Ep> &&__u) noexcept {
      reset(__u.release());
      get_deleter() = std::forward<_Ep>(__u.get_deleter());
      return *this;
    }

    /// Reset the %unique_ptr to empty, invoking the deleter if necessary.
    //对于nullptr_t的赋值操作
    unique_ptr &operator=(nullptr_t) noexcept {
      reset();
      return *this;
    }

    // Observers.

    /// Dereference the stored pointer.
    //对element_type添加一个左值引用
    //在type_traits中定义的add_lvalue_reference的实现细节,在这里并不想多说
    //我可以负责任的告诉你,add_lvalue_reference会对T, T&&变为T &,对T & 不变
    typename add_lvalue_reference<element_type>::type operator*() const {
      __glibcxx_assert(get() != pointer());
      return *get();
    }

    /// Return the stored pointer.
    //->运算符,不用多说,看一下就懂
    pointer operator->() const noexcept {
      _GLIBCXX_DEBUG_PEDASSERT(get() != pointer());
      return get();
    }

    /// Return the stored pointer.
    pointer get() const noexcept { return _M_t._M_ptr(); }

    /// Return a reference to the stored deleter.
    deleter_type &get_deleter() noexcept { return _M_t._M_deleter(); }

    /// Return a reference to the stored deleter.
    const deleter_type &get_deleter() const noexcept {
      return _M_t._M_deleter();
    }

    //隐式转换为bool,可以用在这种场景,例如
    //unique_ptr<Foo> a;
    //if(a) {...}
    /// Return @c true if the stored pointer is not null.
    explicit operator bool() const noexcept {
      return get() == pointer() ? false : true;
    }

    // Modifiers.

    /// Release ownership of any stored pointer.
    pointer release() noexcept { return _M_t.release(); }

    /** @brief Replace the stored pointer.
     *
     * @param __p  The new pointer to store.
     *
     * The deleter will be invoked if a pointer is already owned.
     */
    void reset(pointer __p = pointer()) noexcept {
      static_assert(__is_invocable<deleter_type &, pointer>::value,
                    "unique_ptr's deleter must be invocable with a pointer");
      _M_t.reset(std::move(__p));
    }

    /// Exchange the pointer and deleter with another object.
    void swap(unique_ptr &__u) noexcept {
        //断言检查删除器是否右可交换的能力
      static_assert(__is_swappable<_Dp>::value, "deleter must be swappable");
      //调用_M_t的swap,实际上是__uniq_ptr_impl中的swap
      _M_t.swap(__u._M_t);
    }

    // Disable copy from lvalue.
    //拷贝构造和赋值运算符是删除的
    unique_ptr(const unique_ptr &) = delete;
    unique_ptr &operator=(const unique_ptr &) = delete;
  };

对于非数组版本的unique_ptr我们已经分析完成了,下面是对于数组版本的特例化

相对于非数组版本,数组版本主要修改了

  • 默认删除器做delete[]动作
  • 重载[]
  /// 20.7.1.3 unique_ptr for array objects with a runtime length
  // [unique.ptr.runtime]
  // _GLIBCXX_RESOLVE_LIB_DEFECTS
  // DR 740 - omit specialization for array objects with a compile time length
  // 对编译时期能确定长度的数组特例化
  // 对于已经说过的问题,就不再写注释了,请读者自己注意
  template <typename _Tp, typename _Dp> class unique_ptr<_Tp[], _Dp> {
    template <typename _Up>
    using _DeleterConstraint =
        typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;

    __uniq_ptr_data<_Tp, _Dp> _M_t;

    //就像remove_cv_t
    template <typename _Up> using __remove_cv = typename remove_cv<_Up>::type;

    // like is_base_of<_Tp, _Up> but false if unqualified types are the same
    // 从实现中和注释中就可以看出,需要判断Up是否是Tp的派生类
    template <typename _Up>
    using __is_derived_Tp =
        __and_<is_base_of<_Tp, _Up>,
               __not_<is_same<__remove_cv<_Tp>, __remove_cv<_Up>>>>;

  public:
    using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer;
    using element_type = _Tp;
    using deleter_type = _Dp;

    // helper template for detecting a safe conversion from another
    // unique_ptr
    //辅助模板来帮助实现从另一个unique_ptr转换是否安全
    //安全转换的条件是:
    //是一个数组
    //poniter和element_type*是同一类型
    //另一个unique_ptr::pointer和::element_type*是同一个类型
    //另一个unique_ptr::element_type(*)[]可以转换为当前的unique_ptr::element_type(*)[]
    //至于为什么要这样设计,还是和用多态方式处理数组有关,下面会说到
    template <typename _Up, typename _Ep, typename _UPtr = unique_ptr<_Up, _Ep>,
              typename _UP_pointer = typename _UPtr::pointer,
              typename _UP_element_type = typename _UPtr::element_type>
    using __safe_conversion_up =
        __and_<is_array<_Up>, is_same<pointer, element_type *>,
               is_same<_UP_pointer, _UP_element_type *>,
               is_convertible<_UP_element_type (*)[], element_type (*)[]>>;

    // helper template for detecting a safe conversion from a raw pointer
    // 辅助模板从裸指针转换为当前的unique_ptr::pointer
    //这段代码实在是太难以阅读了,为了方便,我们进行一些格式上的改变
    // template <typename _Up>
    // using __safe_conversion_raw = __and_<
    //     __or_<__or_<is_same<_Up, pointer>, is_same<_Up, nullptr_t>>,
    //           __and_<is_pointer<_Up>, is_same<pointer, element_type *>,
    //                  is_convertible<typename remove_pointer<_Up>::type (*)[],
    //                                 element_type (*)[]>>>>;
    //满足转换规则只需要满足其中以下条件之一(Up是一个raw-pointer)
    //- Up和pointer是同类型或者Up和nullptr_t是同类型
    //- Up是一个指针并且pointer和element_type*是同一类型并且Up移除指针后的类型的数组指针可以转换为element_type的数组指针
    template<typename Up>

    using _safe_conversion_raw = __and_<
            __or_<
                    __or_<is_same<Up, pointer>, is_same<Up, nullptr_t> >,
                    __and_<
                            is_pointer<Up>, is_same<pointer, element_type *>,
                            is_convertible<typename remove_pointer<Up>::type (*)[], element_type (*)[]>
                          >
                 >
    >;
    // Constructors.

    /// Default constructor, creates a unique_ptr that owns nothing.
    template <typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
    constexpr unique_ptr() noexcept : _M_t() {}

    /** Takes ownership of a pointer.
     *
     * @param __p  A pointer to an array of a type safely convertible
     * to an array of @c element_type
     *
     * The deleter will be value-initialized.
     */
    template <
        typename _Up, typename _Vp = _Dp, typename = _DeleterConstraint<_Vp>,
        typename =
            typename enable_if<__safe_conversion_raw<_Up>::value, bool>::type> //做了一个类型约束
    explicit unique_ptr(_Up __p) noexcept : _M_t(__p) {}

    /** Takes ownership of a pointer.
     *
     * @param __p  A pointer to an array of a type safely convertible
     * to an array of @c element_type
     * @param __d  A reference to a deleter.
     *
     * The deleter will be initialized with @p __d
     */
     //从一个raw-pointer和一个删除器构造一个uniuqe_ptr
     //很明显的需要raw-pointer可以安全的转换为unique_ptr并且删除器是可以拷贝狗仔的
    template <typename _Up, typename _Del = deleter_type,
              typename = _Require<__safe_conversion_raw<_Up>, //只是类型约束和非数组版的不一样
                                  is_copy_constructible<_Del>>>
    unique_ptr(_Up __p, const deleter_type &__d) noexcept : _M_t(__p, __d) {}

    /** Takes ownership of a pointer.
     *
     * @param __p  A pointer to an array of a type safely convertible
     * to an array of @c element_type
     * @param __d  A reference to a deleter.
     *
     * The deleter will be initialized with @p std::move(__d)
     */
    template <typename _Up, typename _Del = deleter_type,
              typename = _Require<__safe_conversion_raw<_Up>,
                                  is_move_constructible<_Del>>>
    unique_ptr(
        _Up __p,
        __enable_if_t<!is_lvalue_reference<_Del>::value, _Del &&> __d) noexcept
        : _M_t(std::move(__p), std::move(__d)) {}

    template <typename _Up, typename _Del = deleter_type,
              typename _DelUnref = typename remove_reference<_Del>::type,
              typename = _Require<__safe_conversion_raw<_Up>>>
    unique_ptr(_Up,
               __enable_if_t<is_lvalue_reference<_Del>::value, _DelUnref &&>) =
        delete;

    /// Move constructor.
    unique_ptr(unique_ptr &&) = default;

    /// Creates a unique_ptr that owns nothing.
    template <typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
    constexpr unique_ptr(nullptr_t) noexcept : _M_t() {}

    template <
        typename _Up, typename _Ep,
        typename = _Require<
            __safe_conversion_up<_Up, _Ep>,
            typename conditional<is_reference<_Dp>::value, is_same<_Ep, _Dp>,
                                 is_convertible<_Ep, _Dp>>::type>>
    unique_ptr(unique_ptr<_Up, _Ep> &&__u) noexcept
        : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) {}

    /// Destructor, invokes the deleter if the stored pointer is not null.
    ~unique_ptr() {
      auto &__ptr = _M_t._M_ptr();
      if (__ptr != nullptr)
        get_deleter()(__ptr);
      __ptr = pointer();
    }

    // Assignment.

    /** @brief Move assignment operator.
     *
     * Invokes the deleter if this object owns a pointer.
     */
    unique_ptr &operator=(unique_ptr &&) = default;

    /** @brief Assignment from another type.
     *
     * @param __u  The object to transfer ownership from, which owns a
     *             convertible pointer to an array object.
     *
     * Invokes the deleter if this object owns a pointer.
     */
    template <typename _Up, typename _Ep>
    typename enable_if<__and_<__safe_conversion_up<_Up, _Ep>,
                              is_assignable<deleter_type &, _Ep &&>>::value,
                       unique_ptr &>::type
    operator=(unique_ptr<_Up, _Ep> &&__u) noexcept {
      reset(__u.release());
      get_deleter() = std::forward<_Ep>(__u.get_deleter());
      return *this;
    }

    /// Reset the %unique_ptr to empty, invoking the deleter if necessary.
    unique_ptr &operator=(nullptr_t) noexcept {
      reset();
      return *this;
    }

    // Observers.
    // 重载的[],实现也是非常简单
    /// Access an element of owned array.
    typename std::add_lvalue_reference<element_type>::type
    operator[](size_t __i) const {
      __glibcxx_assert(get() != pointer());
      return get()[__i];
    }

    /// Return the stored pointer.
    pointer get() const noexcept { return _M_t._M_ptr(); }

    /// Return a reference to the stored deleter.
    deleter_type &get_deleter() noexcept { return _M_t._M_deleter(); }

    /// Return a reference to the stored deleter.
    const deleter_type &get_deleter() const noexcept {
      return _M_t._M_deleter();
    }

    /// Return @c true if the stored pointer is not null.
    explicit operator bool() const noexcept {
      return get() == pointer() ? false : true;
    }

    // Modifiers.

    /// Release ownership of any stored pointer.
    pointer release() noexcept { return _M_t.release(); }

    /** @brief Replace the stored pointer.
     *
     * @param __p  The new pointer to store.
     *
     * The deleter will be invoked if a pointer is already owned.
     */
    template <
        typename _Up,
        typename = _Require<__or_<
            is_same<_Up, pointer>,
            __and_<is_same<pointer, element_type *>, is_pointer<_Up>,
                   is_convertible<typename remove_pointer<_Up>::type (*)[],
                                  element_type (*)[]>>>>>
    void reset(_Up __p) noexcept {
      _M_t.reset(std::move(__p));
    }

    void reset(nullptr_t = nullptr) noexcept { reset(pointer()); }

    /// Exchange the pointer and deleter with another object.
    void swap(unique_ptr &__u) noexcept {
      static_assert(__is_swappable<_Dp>::value, "deleter must be swappable");
      _M_t.swap(__u._M_t);
    }

    // Disable copy from lvalue.
    unique_ptr(const unique_ptr &) = delete;
    unique_ptr &operator=(const unique_ptr &) = delete;
  };

std::make_unique

令人头大的部分终于过去了,占据了本文章绝大篇幅的那些部分。接下来放松一下,看一看std::make_unique的实现吧

在这里仅仅分析14的部分(make_unique是14增添的),对于14以上的代码,读者有兴趣可以自行了解

_MakeUniq

做的功能就是类型萃取,用作于make_unique的返回值,这样做的目的也是为了实现类型安全

类型参数应为T的地方写成T[]会导致类型替换失败,反之亦然

  template<typename _Tp>
    struct _MakeUniq
    { typedef unique_ptr<_Tp> __single_object; };

  template<typename _Tp>
    struct _MakeUniq<_Tp[]>
    { typedef unique_ptr<_Tp[]> __array; };

  template<typename _Tp, size_t _Bound>
    struct _MakeUniq<_Tp[_Bound]>
    { struct __invalid_type { }; };

make_unique的实现部分

  /// std::make_unique for single objects
  // 非数组版本
  template<typename _Tp, typename... _Args>
    inline typename _MakeUniq<_Tp>::__single_object
    make_unique(_Args&&... __args) //&& ... +forward 的形式转发参数
    { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }

  /// std::make_unique for arrays of unknown bound
  //返回一个unique_ptr指向一个长度为__num的数组
  //关于remove_extent_t,请参考https://www.apiref.com/cpp-zh/cpp/types/remove_extent.html
  template<typename _Tp>
    inline typename _MakeUniq<_Tp>::__array
    make_unique(size_t __num)
    { return unique_ptr<_Tp>(new remove_extent_t<_Tp>[__num]()); }
  
  //不能从一个已知界限的数组返回一个unique_ptr
  /// Disable std::make_unique for arrays of known bound
  template<typename _Tp, typename... _Args>
    typename _MakeUniq<_Tp>::__invalid_type
    make_unique(_Args&&...) = delete;

总结

unique_ptr的源码分析,就告一段落了。还有很多很多细节和实现手法没有剖析,剖析了的细节和实现手法,一些也没有说明为什么要这样做。

如果日后发现的错误,会在这里更新勘误表。

如果你有任何的问题,可以通过邮箱,QQ来联系到我。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

unique-ptr源码解析 的相关文章

随机推荐

  • Vue 项目中使用ElementUI 框架中message 对话框实例对象

    Element ui中message弹出框使用总结 第一 按需导入的element ui中的message 在customer vue 文件中引入message 弹出对话框 第二步 在方法里面直接通过调用Message 弹出对话框实例对象
  • 【软件测试】

    系列文章目录 文章目录 系列文章目录 前言 第四章 单元测试 4 1 软件测试过程概述 4 2 什么是单元测试 4 2 1 单元测试的定义 4 2 2 单元测试的重要性 4 2 3 单元测试原则 4 3 单元测试的目标和任务 4 3 1 单
  • 开发工程师VS测试工程师VS测试开发工程师

    每年正式上班之后就会非常忙 今年也不例外 我们公司现在也忙了起来 都没有时间写我的自动化测试教程了 不过大家放心 我会继续写下去的 不过可能更新的不那么快了 最近被同事问到了一个问题 开发 测试和测试工程师都有啥区别 开发转测试是不是比我们
  • ISP之LSC(Lens Shading Correction)

    LSC Lens Shading Correction即镜头暗影校正 一 LSC的意义 众所周知Lens Shading分为Luma Shading和Color Shading 一般来说 物体到Lens中心的距离越远 图像越暗 呈圆形中性对
  • NC 和NCC 用户被锁定

    NC账户被锁定 NC上的用户 不管是管理员 还是用户 都是统一存放在同一张表的 不像NCC 一样有sm super user这张表 用来区分 所以在NC上一视同仁就好了 以下有两种常用的修改方式 一 数据库修改 nc65用户被锁定后涉及到三
  • 【Nginx】Nginx新增自定义模块

    Nginx新增自定义模块 系统环境 Nginx模块分类 Nginx模块执行流程 Nginx Handler模块示例 Nginx filter模块示例 系统环境 uname a Linux localhost localdomain 3 10
  • 爬虫 - QS世界大学排名数据

    爬虫 QS世界大学排名数据 网站简介 爬虫方法概述 使用工具 爬虫概述 第一部分 导入需要用到的python包 设置selenium控制浏览器打开网页 控制鼠标操作 定位节点 提取数据 滚轮翻页 构建循环自动爬取数据 数据储存 第二部分 导
  • 【图像处理OpenCV(C++版)】——2.2 OpenCV之矩阵运算详解(全)

    前言 欢迎来到本博客 本专栏主要结合OpenCV和C 来实现一些基本的图像处理算法并详细解释各参数含义 适用于平时学习 工作快速查询等 随时更新 具体食用方式 可以点击本专栏 OpenCV快速查找 更新中 gt 搜索你要查询的算子名称或相关
  • Mac 终端进入 conda 虚拟环境后 pip 依然安装到全局下的问题解决

    一 问题起因 之前折腾安装各种软件 可能是不小心改了些什么莫名奇妙的设置 然后就出现了问题 mac 系统 Catalina 版本10 15 5 在 anaconda 中创建了新的虚拟环境 比如 test 然后在 mac 终端中 输入sour
  • HC32F003系列芯片时钟源性能测试及分析

    HC32F003系列芯片时钟源性能测试及分析 测试概要 测试目的 分析HC32F003系列芯片几种时钟源的性能差异 主要分析频率 占空比的误差范围 测试项目 分别测试以下几种时钟源的性能 每种测试不少于10次 内部高速4MHz 内部高速8M
  • 服务器网络请求返回状态码集合

    在开发过程中报错是最令人头疼的 接下来我们就来谈谈那些状态码都是什么 200 服务器成功返回请求的数据 201 新建或修改数据成功 202 一个请求已经进入后台排队 异步任务 204 删除数据成功 400 发出的请求有错误 服务器没有进行新
  • 5G全产业链最新解读

    来源 中创产业研究院 摘要 自5G概念的提出 各国相关技术的研发以及产业布局也在如火如荼进行之中 与此同时我国5G在标准研发上正逐渐成为全球领跑者 有望在2019年实现5G技术的试商用 在2020年实现正式商用 本文将围绕5G的概况 国内外
  • pyinstaller在x86环境安装与多文件打包

    一 安装 Python官网下载安装源码 或者使用pip install pyinstaller安装 源码安装 解压后 进入文件夹 执行 python setup py install进行安装 二 多文件打包 方法主要还是两个 1 还是直接使
  • 6. Redis缓存设计与性能优化

    分布式缓存技术Redis 1 多级缓存架构 2 缓存设计 2 1 缓存穿透 2 2 缓存失效 击穿 2 3 缓存雪崩 2 4 热点缓存key重建优化 2 5 缓存与数据库双写不一致 3 开发规范与性能优化 3 1 键值设计 3 1 1 ke
  • 数据库系统笔记1: 绪论

    数据库概述 DBMS Data Base Management System 数据库管理系统 Metadata 元数据 关于数据描述的数据 数据模型 层次模型 使用树状结构表示实体和实体之间的联系 网状模型 使用有向图表示实体和实体之间的联
  • Iterator、Iterable接口的使用及详解

    Java集合类库将集合的接口与实现分离 同样的接口 可以有不同的实现 Java集合类的基本接口是Collection接口 而Collection接口必须继承java lang Iterable接口 以下图表示集合框架的接口 java lan
  • MFE常用数据结构之Lattice

    今天看了DUFFY的C For FE中关于介绍Lattice的相关内容 为了表示对原作者的尊敬 首先我还是引用一下作者关于Lattice Structures的介绍的原话 Lattice structures are well known
  • 软件工程基础知识--软件过程模型

    软件过程模型习惯上也称为软件开发模型 它是软件开发全部过程 活动和任务的结构框架 典型的软件过程模型有瀑布模型 增量模型 演化模型 原型模型 螺旋模型 喷泉模型 基于构件的开发模型和形式化方法模型等 瀑布模型 该模型给出了软件生存周期各阶段
  • 编程语言的一些基础概念(三):面向对象

    在前面两篇中 主要讲了函数式编程语言的一些基础概念 这篇是 Coursera Programming Languages Part C 的总结 通过 Ruby 介绍面向对象编程里的一些概念 了解这些概念能让你在上手任何一门新的面向对象语言时
  • unique-ptr源码解析

    title unique ptr源码解析 date 2022 09 22 21 00 56 tags Modern C C C Library 前言 这篇博客是对unique ptr源代码的分析 本文使用的编译器是MinGW 本篇文章不保证