unique_ptr 源码分析

本文详细介绍了C++标准库中unique_ptr的设计与实现细节,包括其构造、析构、移动等核心功能,以及内部数据结构的封装方式。文章还探讨了不同情况下unique_ptr的行为表现,并解释了如何使用自定义析构器。
  1. unique_ptr

    • 简介

      • 独占

      • 实现了构造,实现了移动.

      • 不允许拷贝.

    • 实现

      • unique_ptr提供了指针拥有的接口

      • __uniq_ptr_impl提供的是数据和析构类.

  2. 源码

    • 源码

       template <typename _Tp, typename _Dp = default_delete<_Tp>>
         class unique_ptr
         {
           template <class _Up>
           using _DeleterConstraint =
         typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
      
           __uniq_ptr_impl<_Tp, _Dp> _M_t;
      
         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
           template<typename _Up, typename _Ep>
         using __safe_conversion_up = __and_<
                 is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
                     __not_<is_array<_Up>>,
                     __or_<__and_<is_reference<deleter_type>,
                                  is_same<deleter_type, _Ep>>,
                           __and_<__not_<is_reference<deleter_type>>,
                                  is_convertible<_Ep, deleter_type>>
                     >
                   >;
      
           // Constructors.
      
           /// Default constructor, creates a unique_ptr that owns nothing.
           template <typename _Up = _Dp,
             typename = _DeleterConstraint<_Up>>
         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 _Up = _Dp,
             typename = _DeleterConstraint<_Up>>
         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
            */
           unique_ptr(pointer __p,
           typename conditional<is_reference<deleter_type>::value,
             deleter_type, const deleter_type&>::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 deleter.
            *
            * The deleter will be initialized with @p std::move(__d)
            */
           unique_ptr(pointer __p,
           typename remove_reference<deleter_type>::type&& __d) noexcept
           : _M_t(std::move(__p), std::move(__d))
           { static_assert(!std::is_reference<deleter_type>::value,
                   "rvalue deleter bound to reference"); }
      
           /// Creates a unique_ptr that owns nothing.
           template <typename _Up = _Dp,
             typename = _DeleterConstraint<_Up>>
         constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
      
           // Move constructors.
      
           /// Move constructor.
           unique_ptr(unique_ptr&& __u) noexcept
           : _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
      
           /** @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.
            */
           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()))
         { }
      
      #if _GLIBCXX_USE_DEPRECATED
      #pragma GCC diagnostic push
      #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
           /// Converting constructor from @c 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
           {
         auto& __ptr = _M_t._M_ptr();
         if (__ptr != nullptr)
           get_deleter()(__ptr);
         __ptr = pointer();
           }
      
           // Assignment.
      
           /** @brief Move assignment operator.
            *
            * @param __u  The object to transfer ownership from.
            *
            * Invokes the deleter first if this object owns a pointer.
            */
           unique_ptr&
           operator=(unique_ptr&& __u) noexcept
           {
         reset(__u.release());
         get_deleter() = std::forward<deleter_type>(__u.get_deleter());
         return *this;
           }
      
           /** @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 first 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.
      
           /// Dereference the stored pointer.
           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(); }
      
           /// 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
           {
         pointer __p = get();
         _M_t._M_ptr() = pointer();
         return __p;
           }
      
           /** @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
           {
         using std::swap;
         swap(_M_t._M_ptr(), __p);
         if (__p != pointer())
           get_deleter()(__p);
           }
      
           /// Exchange the pointer and deleter with another object.
           void
           swap(unique_ptr& __u) noexcept
           {
         using std::swap;
         swap(_M_t, __u._M_t);
           }
      
           // Disable copy from lvalue.
           unique_ptr(const unique_ptr&) = delete;
           unique_ptr& operator=(const unique_ptr&) = delete;
       };
      
      
    • 数据封装

       template <typename _Tp, typename _Dp>
         class __uniq_ptr_impl
         {
           template <typename _Up, typename _Ep, typename = void>
       struct _Ptr
       {
         using type = _Up*;
       };
      
           template <typename _Up, typename _Ep>
       struct
       _Ptr<_Up, _Ep, __void_t<typename remove_reference<_Ep>::type::pointer>>
       {
         using type = typename remove_reference<_Ep>::type::pointer;
       };
      
         public:
           using _DeleterConstraint = enable_if<
             __and_<__not_<is_pointer<_Dp>>,
              is_default_constructible<_Dp>>::value>;
      
           using pointer = typename _Ptr<_Tp, _Dp>::type;
      
           __uniq_ptr_impl() = default;
           __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)) { }
      
           pointer&   _M_ptr() { return std::get<0>(_M_t); }
           pointer    _M_ptr() const { return std::get<0>(_M_t); }
           _Dp&       _M_deleter() { return std::get<1>(_M_t); }
           const _Dp& _M_deleter() const { return std::get<1>(_M_t); }
      
         private:
           tuple<pointer, _Dp> _M_t;
         };
      
      
      • 首先是用tuple进行的数据封装.

      • tuple有个特性就是,对于空类不占内存,非空类会占用sizeof那么大的内存.

      • 具体可以看tuple源码和模板元编程.

      • 总之,数据采用并排的方式,空类不占用空间,非空类占用析构.所以非空析构对象则会占用空间.

  3. 析构

    • 默认析构

       template<typename _Tp>
         struct default_delete
         {
           /// Default constructor
           constexpr default_delete() noexcept = default;
      
           /** @brief Converting constructor.
            *
            * Allows conversion from a deleter for arrays of another type, @p _Up,
            * only if @p _Up* is convertible to @p _Tp*.
            */
           template<typename _Up, typename = typename
              enable_if<is_convertible<_Up*, _Tp*>::value>::type>
             default_delete(const default_delete<_Up>&) noexcept { }
      
           /// Calls @c delete @p __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;
           }
         };
      
      
    • 自定义析构

      • 通过tuple组合起来.数据以成员变量的形式保存.
<think>好的,用户提供的代码是`std::make_unique`的实现片段,看起来他们想深入了解这个函数的工作原理以及它如何确保异常安全。用户之前的问题讨论了`make_unique`相比直接使用`new`的优势,特别是异常安全方面。现在他们可能希望从源码层面进一步理解其实现机制。 首先,我需要确认用户的具体需求。他们贴出了`make_unique`的模板代码,可能希望了解这段代码是如何工作的,以及为什么它能够保证异常安全。用户可能已经知道`make_unique`的优点,但想深入内部实现,特别是结合之前的讨论中提到的异常处理机制。 接下来,我需要分析代码的结构。代码中的模板函数`make_unique`接受类型`_Tp`和构造参数`_Args`,然后返回一个`unique_ptr`。函数体内,它创建了一个`_Tp`类型的对象,并将其指针传递给`unique_ptr`的构造函数。这里的关键点在于,`make_unique`如何将资源分配和所有权转移结合在一起,避免中间状态。 用户可能想知道为什么这样的实现能够避免资源泄漏。根据之前的讨论,当使用裸指针`new`时,如果在分配内存后但在构造`unique_ptr`之前抛出异常,会导致泄漏。而`make_unique`将这两个步骤合并为一个原子操作,确保即使抛出异常,资源也会被正确释放。 另外,用户可能对代码中的宏如`_GLIBCXX23_CONSTEXPR`、`__detail::__unique_ptr_t<_Tp>`感兴趣。这些是GCC库的实现细节,可能需要解释它们的作用,但用户可能更关注整体机制而非具体编译器宏。 还需要考虑用户可能的疑问点,比如转发参数如何工作(`std::forward<_Args>(__args)...`),以及模板参数推导如何简化使用。此外,用户可能想知道这个函数在C++14中引入的意义,以及它与C++11之前的版本有何不同。 最后,用户可能希望得到一个总结,强调为什么使用`make_unique`是最佳实践,并可能提出相关问题,如异常安全、自定义删除器、多线程使用等。需要确保回答覆盖代码结构、异常安全机制、参数转发、版本支持,并生成相关问题。 </think>### `std::make_unique` 源码解析与异常安全性详解 --- #### **1. 源码结构解析** ```cpp template <typename _Tp, typename... _Args> _GLIBCXX23_CONSTEXPR inline __detail::__unique_ptr_t<_Tp> make_unique(_Args &&...__args) { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); } ``` **关键元素**: - **模板参数**: - `_Tp`:目标类型(非数组类型,需显式使用 `make_unique<T[]>` 管理数组)。 - `_Args...`:构造参数的可变模板参数包。 - **返回类型**: - `__detail::__unique_ptr_t<_Tp>`:实际是 `std::unique_ptr<_Tp>` 的别名,用于封装返回类型。 - **参数转发**: - `std::forward<_Args>(__args)...`:完美转发构造参数,避免额外拷贝或移动。 --- #### **2. 核心机制:原子性资源分配** ```cpp return unique_ptr<_Tp>(new _Tp(...)); ``` - **原子操作**:资源分配(`new _Tp(...)`)与所有权绑定(`unique_ptr` 构造)在**同一表达式**中完成。 - **异常安全性**: - 如果 `new _Tp(...)` 抛出异常(如构造函数失败),`unique_ptr` 尚未构造,内存会自动释放(栈展开机制触发 `operator delete`)。 - 如果 `std::forward` 转发参数过程中抛出异常(如参数构造失败),`new _Tp` 尚未执行,无资源泄漏。 --- #### **3. 异常安全性对比:`make_unique` vs `new`** | 操作方式 | 异常安全保证 | 资源泄漏风险 | 示例代码 | |---------------------------|--------------|--------------|--------------------------------------| | `make_unique<T>(args...)` | ✅ 完全安全 | ❌ 无 | `auto p = make_unique<Widget>(x, y);`| | `unique_ptr<T>(new T(...))`| ❌ 风险 | ✅ 存在 | `auto p(new T(x));` | **风险场景**: ```cpp // ❌ 不推荐:中间状态导致泄漏 auto p = std::unique_ptr<Widget>(new Widget()); // 如果 new Widget() 成功,但后续操作(如函数调用)抛出异常,p 未完全构造,资源泄漏 ``` --- #### **4. 参数完美转发机制** ```cpp std::forward<_Args>(__args)... ``` - **作用**:将参数以原始值类型(左值/右值)传递给构造函数。 - **示例**: ```cpp struct S { S(int&, std::string&&); }; int x = 42; auto p1 = std::make_unique<S>(x, std::string("tmp")); // x 作为左值,string 作为右值传递 auto p2 = std::make_unique<S>(42, "tmp"); // 42 和 "tmp" 转换为构造函数参数 ``` --- #### **5. 特殊类型支持** - **数组类型**:需使用 `std::make_unique<T[]>(size)`。 ```cpp auto arr = std::make_unique<int[]>(10); // 管理 int[10],析构时调用 delete[] ``` - **自定义删除器**:`make_unique` 不支持直接指定删除器,需手动构造 `unique_ptr`。 ```cpp auto deleter = [](FILE* f) { if (f) fclose(f); }; std::unique_ptr<FILE, decltype(deleter)> file(fopen("test.txt", "r"), deleter); ``` --- #### **6. C++14 引入的意义** - **简化代码**:避免冗余的 `new` 和类型重复。 ```cpp // C++11 及之前 auto p = std::unique_ptr<Widget>(new Widget(x, y)); // C++14 起 auto p = std::make_unique<Widget>(x, y); ``` - **强制异常安全**:通过封装确保资源分配与所有权绑定的原子性。 --- ### **总结:`make_unique` 的核心优势** | 优势 | 说明 | |---------------------|----------------------------------------------------------------------| | **异常安全** | 资源分配与所有权绑定原子化,避免中间状态泄漏 | | **代码简洁性** | 无需显式调用 `new`,减少冗余 | | **完美转发** | 支持任意参数构造目标类型 | | **类型推导** | 模板参数自动推导,避免显式指定类型 | | **数组特化支持** | 提供 `make_unique<T[]>` 专门管理数组资源 | --- ### **相关问题** 1. `std::make_shared` 的实现是否与 `std::make_unique` 类似? 2. 如何为 `unique_ptr` 编写支持异常安全的自定义删除器? 3. `std::forward` 在参数转发中如何避免类型退化? 4. 在 C++11 中如何模拟 `make_unique` 的异常安全行为? 5. 使用 `make_unique` 管理动态数组时需要注意哪些细节?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值