STL中的智能指针(Smart Pointer)及其源码剖析: std::unique_ptr

STL中的智能指针(Smart Pointer)及其源码剖析: std::unique_ptr

  • std::auto_ptr一样,std::unique_ptr也是一种智能指针,它也是通过指针的方式来管理对象资源,并且在 unique_ptr 的生命期结束后释放该资源。
  • unique_ptr 持有对对象的独有权 —— 两个 unique_ptr 不能指向一个对象,不能进行复制操作只能进行移动操作。
  • 当发生下列情况是, unique_ptr 会所管理的指针使用其关联的 deleter
    a. 负责管理的 unique_ptr 对象被销毁时;
    b. 负责管理的 unique_ptr 对象通过 operator=reset 函数赋值给另一个指针。

一. unique_ptr 的使用

1. unique_ptr 的声明

  // since C++11

  template<class T, class Deleter = std::default_delete<T>>        (1)
  class unique_ptr;

  template<class T, class Deleter>                                 (2)
  class unique_ptr<T[], Deleter>;

(1) 管理单个对象泛化的 unique_ptr 模板的声明。
(2) 对 (1) 版本, 针对对象数组进行特化后的版本的声明。

2. unique_ptr 的构造函数

  • unique_ptr<T> 的构造函数。
  constexpr unique_ptr();                                        (1)
  constexpr unique_ptr(nullptr_t);

  explicit unique_ptr(pointer p);                                (2) 

  unique_ptr(pointer p,                                          (3)
      typename conditional<is_reference<Deleter>::value,
      Deleter, const Deleter&>::type d);                 

  unique_ptr(pointer p,                                          (4)  
      typename remove_reference<Deleter>::type&& d);     

  unique_ptr(unique_ptr&& u);                                    (5)                           

  template<class U, class E>                                     (6)
  unique_ptr(unique_ptr<U, E>&& u);

  template<class U>                                              (7)
  unique_ptr(auto_ptr<U>&& u);

(1) 构造一个没有管理任何资源的 std::unique_ptr 对象。 这里的 nullptr_t 是从 C++11 开始新增的类型, 表示空指针(nullptr)的类型。
(2) 构造一个管理 p 指向资源的 std::unique_ptr 对象。
(3) 构造一个管理 p 指向资源的 std::unique_ptr 对象, 同时将释放资源的函数设置为 d
(4) 构造一个管理 p 指向资源的 std::unique_ptr 对象, 同时将释放资源的函数设置为 d。[主要针对 (3) 的 d 是右值引用类型时的重载]

  • conditional: 在编译期,根据条件 B 进行 typedeftemplate< bool B, class T, class F > struct conditional; 中, 如果 B 是 true, 则定义为 T 类型, 如果 B 是 false, 则定义为 F 类型。
  • is_reference:在编译期,判断某个类型是否是引用类型。
  • remove_reference:在编译期移除某个类型的引用符号。如: remove_reference<int&>::type 类型就是 int 类型。
  • (3)(4)中的 d 的类型推导结果列举如下:
    a. 当 Deleter 是非引用类型 A 时,
    C++
    unique_ptr(pointer p, const A& d); (3)
    unique_ptr(pointer p, A&& d); (4)

    b. 当 Deleter 是左值引用类型 A& 时,
    C++
    unique_ptr(pointer p, A& d); (3)
    unique_ptr(pointer p, A&& d); (4)

    c. 当 Deleter 是 常左值引用类型 const A&时,
    C++
    unique_ptr(pointer p, const A& d); (3)
    unique_ptr(pointer p, const A&& d); (4)

(5) 利用移动语义,解决了 auto_ptr 在构造函数上不足的地方。移动构造函数更能体现 unique_ptr 在赋值时, 对资源的管理权的移交。
(6) 与 (5) 类似。主要针对隐式类型转化的情况。
(7) 实现从 auto_ptrunique_ptr 的构造或赋值。

  • unique_ptr<T[]> 的构造函数。
    (略, 具体与 unique_ptr<T> 类似,细节处有略微差异)

  • 例子(取自 cppreference.com)

#include <iostream>
#include <memory>

struct Foo { // object to manage
    Foo() { std::cout << "Foo ctor\n"; }
    Foo(const Foo&) { std::cout << "Foo copy ctor\n"; }
    Foo(Foo&&) { std::cout << "Foo move ctor\n"; }
    ~Foo() { std::cout << "~Foo dtor\n"; }
};

struct D { // deleter
    D() {};
    D(const D&) { std::cout << "D copy ctor\n"; }
    D(D&) { std::cout << "D non-const copy ctor\n";}
    D(D&&) { std::cout << "D move ctor \n"; }
    void operator()(Foo* p) const {
        std::cout << "D is deleting a Foo\n";
        delete p;
    };
};

int main()
{
    std::cout << "Example constructor(1)...\n";
    std::unique_ptr<Foo> up1;  // up1 is empty
    std::unique_ptr<Foo> up1b(nullptr);  // up1b is empty

    std::cout << "\nExample constructor(2)...\n";
    {
        std::unique_ptr<Foo> up2(new Foo); //up2 now owns a Foo
    } // Foo deleted

    std::cout << "\nExample constructor(3)...\n";
    D d;
    {  // deleter type is not a reference
       std::unique_ptr<Foo, D> up3(new Foo, d); // deleter copied
    }
    {  // deleter type is a reference 
       std::unique_ptr<Foo, D&> up3b(new Foo, d); // up3b holds a reference to d
    }

    std::cout << "\nExample constructor(4)...\n";
    {  // deleter is not a reference 
       std::unique_ptr<Foo, D> up4(new Foo, D()); // deleter moved
    }

    std::cout << "\nExample constructor(5)...\n";
    {
       std::unique_ptr<Foo> up5a(new Foo);
       std::unique_ptr<Foo> up5b(std::move(up5a)); // ownership transfer
    }

    std::cout << "\nExample constructor(6)...\n";
    {
        std::unique_ptr<Foo, D> up6a(new Foo, d); // D is copied
        std::unique_ptr<Foo, D> up6b(std::move(up6a)); // D is moved

        std::unique_ptr<Foo, D&> up6c(new Foo, d); // D is a reference
        std::unique_ptr<Foo, D> up6d(std::move(up6c)); // D is copied
    }

    std::cout << "\nExample constructor(7)...\n";
    {
        std::auto_ptr<Foo> up7a(new Foo);
        std::unique_ptr<Foo> up7b(std::move(up7a)); // ownership transfer
    }
}

执行结果:

SmartPtr_unique_ptr_test.png

3. unique_ptr的析构函数: 销毁管理的对象。

  ~unique_ptr();

如果 *this 有管理的资源,则用 deleter 销毁该资源。

4. 拷贝赋值函数

  • unique_ptr<T> 的拷贝赋值函数。
  unique_ptr& opertor=(unique_ptr&& r);           (1)

  template<class U, class E>
  unique_ptr& operator=(unique_ptr<U, E>&& r);    (2)

  unique_ptr& operator=(nullptr_t);               (3)

(1) 将 r 管理的资源的控制权移交给 *this,本身有管理的资源,则先用 Deleter 释放该资源。[这里就是右值引用的好处了,曾记否,只有左值引用时代的 auto_ptr实现这个操作是多么的复杂。]
(2) 类似于 (1), 不过是针对 可隐式转换为该类型的 r
(3) 相当于调用 reset, 将管理资源的指针设为空。

  • unique_ptr<T[]> 的拷贝赋值函数。
    (略, 具体与 unique_ptr<T> 类似,细节处有略微差异)

  • 例子(改自 cppreference.com)

#include <iostream>
#include <memory>

struct Foo {
    Foo() { std::cout << "Foo\n"; }
    ~Foo() { std::cout << "~Foo\n"; }
};

int main() 
{
    std::unique_ptr<Foo> p1;

    {
        std::cout << "Creating new Foo...\n";
        std::unique_ptr<Foo> p2(new Foo);

        // p1 = p2; // Error ! can't copy unique_ptr
        //unique_ptr& opertor=(unique_ptr&& r);
        p1 = std::move(p2);
        std::cout << "About to leave inner block...\n";

        // Foo instance will continue to live, 
        // despite p2 going out of scope
    }

    // unique_ptr& operator=(nullptr_t);
    std::cout << "Creating new Foo...\n"; 
    std::unique_ptr<Foo> p3(new Foo); 
    std::cout << "Before  p3 = nullptr...\n"; 
    p3 = nullptr;
    std::cout << "After  p3 = nullptr...\n"; 

    std::cout << "About to leave program...\n";
}

执行结果:

    Creating new Foo...
    Foo
    About to leave inner block...
    Creating new Foo...
    Foo
    Before  p3 = nullptr...
    ~Foo
    After  p3 = nullptr...
    About to leave program...
    ~Foo

6. 其他成员函数(unique_ptr::release, unique_ptr::reset, unique_ptr::swap, unique_ptr::get, unique_ptr::get_deleter, unique_ptr::operator bool, unique_ptr::operator*、unique_ptr::operator->, )

主要针对 unique_ptr<T>, 特化版本 unique_ptr<T> 与之类似。

  pointer release();                            (1)

  void reset(pointer ptr = pointer());          (2)

  void swap(unique_ptr& other);                 (3)

  pointer get() const;                          (4)

  Deleter& get_deleter();                       (5)
  const Deleter& get_deleter() const;           (6)

  explicit operator bool() const;               (7)

  typename std::add_lvalue_reference<T>::type   (8)
    operator*() const;
  pointer operator->() const;                   (9)

(1) 移交出 *this 管理资源的指针。如果 *this 没有管理资源,则返回 nullptr。
(2) 设置 *this 管理 ptr 指向的资源, 如果 *this 本身有管理的资源,则先用 deleter 释放该资源。
(3) 将 *this 管理的资源和 other 管理的资源进行交换。
(4) 获取 *this 管理资源的指针,如果没有被管理的资源则返回 nullptr
(5) 获取 *this 绑定的 deleter
(6) 与 (5) 类似。针对常类型。
(7) 隐式转换函数。判断是否 *this 管理有资源,如果是,则返回 true, 否则返回 false

关键字 explicit 用在隐式转换函数前,限制该隐式转换函数,使其不能进行 copy initialization。例如:

 struct A
 {
   operator bool() const { return true; }
 };

 struct B
 {
   explicit operator bool() const { return true; 
 };

 int main()
 {
   A a1;
   bool na1 = a1; // OK: copy-initialization selects A::operator bool()
   bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization

   B b2; 
   if (b2) ;      // OK: B::operator bool()
   //  bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
   bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization
   }

(8) 提供类似于指针的接口。获取 *this 所管理的对象的左值引用。如果 *this 没有管理对象,则该未定义该行为。
(9) 提供类似于指针的接口。获取 *this 所管理的对象的指针。如果 *this 没有管理对象,则该未定义该行为。

  • 例子
#include <iostream>
#include <memory>
#include <string>

using namespace std;

struct Foo {
    Foo(string name = "Foo") : m_name(name)
     { std::cout << m_name << " is creating...\n"; }

    ~Foo() { std::cout << m_name << " is deleting...\n"; }
    string m_name;
};

class FooDeleter{
public:
    void Show() const { cout << "I'm FooDeleter..." << endl; }
    void operator()(Foo* pf) const{
        cout << "FooDeleter is deleting " << pf->m_name << endl;
        delete pf;
    }   
};
int main() 
{
    {
        // for realese()...
        cout << "test for release()..." << endl << endl;
        unique_ptr<Foo, FooDeleter> f1(new Foo("Foo f1"));
        Foo* pf1 = f1.release(); // 将资源的管理权交给 pf1

        cout << "before delete pf1..." << endl; 
        delete pf1;  
        cout << "after delete pf1..." << endl << endl; 
   }
   cout << "--------------------------------" << endl << endl;
   {
        // for reset()...
        cout << "test for reset()..." << endl << endl;
        unique_ptr<Foo, FooDeleter> f2(new Foo("Foo f2"));
        Foo* pf2 = new Foo("Foo pf2");

        cout << "before f2.reset(pf2)..." << endl; 
        f2.reset(pf2); 
        cout << "after f2.reset(pf2)..." << endl << endl; 
  }
  cout << "--------------------------------" << endl << endl;
  {
    // for swap()...
    cout << "test for swap()..." << endl << endl;
    unique_ptr<Foo, FooDeleter> f3(new Foo("Foo f3"));
    unique_ptr<Foo, FooDeleter> f4(new Foo("Foo f4"), FooDeleter());

    f3.swap(f4);

    cout << "before f3.reset(nullptr)..." << endl; 
    f3.reset(nullptr); 
    cout << "after f3.reset(nullptr)..." << endl << endl;

    cout << "before f4.reset(nullptr)..." << endl; 
    f4.reset(nullptr); 
    cout << "after f4.reset(nullptr)..." << endl << endl;

  }
  cout << "--------------------------------" << endl << endl;
  {
    // for get(), get_deleter(), operator bool(), operator*() and operator->()...
    cout << "test for get(), get_deleter(), operator bool()" << endl
       << "operator*() and operator->()..." << endl << endl;

    unique_ptr<Foo, FooDeleter> f5(new Foo("Foo f5"));
    unique_ptr<Foo, FooDeleter> defaultF;

    // for get()...
    Foo* pf3 = f5.get();
    cout << " f5.get()->m_name = " << pf3->m_name << endl;

    // for get_deleter()...
    FooDeleter& pd1 = f5.get_deleter();
    pd1.Show();

    // for operator bool()
    if(f5) cout << "f5 == true" << endl;
    if(!defaultF) cout << "defaultF == false" << endl;

    // for operator*() and operator->()...
    cout << "f5->m_name == " << f5->m_name << endl;
    cout << "(*f5).m_name == " << (*f5).m_name << endl;
  }
  cout << "--------------------------------" << endl << endl;
}

运行结果:

test for release()...

Foo f1 is creating...
before delete pf1...
Foo f1 is deleting...
after delete pf1...

--------------------------------

test for reset()...

Foo f2 is creating...
Foo pf2 is creating...
before f2.reset(pf2)...
FooDeleter is deleting Foo f2
Foo f2 is deleting...
after f2.reset(pf2)...

FooDeleter is deleting Foo pf2
Foo pf2 is deleting...
--------------------------------

test for swap()...

Foo f3 is creating...
Foo f4 is creating...
before f3.reset(nullptr)...
FooDeleter is deleting Foo f4
Foo f4 is deleting...
after f3.reset(nullptr)...

before f4.reset(nullptr)...
FooDeleter is deleting Foo f3
Foo f3 is deleting...
after f4.reset(nullptr)...

--------------------------------

test for get(), get_deleter(), operator bool()
operator*() and operator->()...

Foo f5 is creating...
 f5.get()->m_name = Foo f5
I'm FooDeleter...
f5 == true
defaultF == false
f5->m_name == Foo f5
(*f5).m_name == Foo f5
FooDeleter is deleting Foo f5
Foo f5 is deleting...
--------------------------------

二. unique_ptr 源码剖析(源码出自 Dev C++)

1. 辅助类 template<typename _Tp> struct default_delete 的源码

default_deleteunique_ptr 绑定的默认 deleter。下面解析的是其泛化版本的源码(限于篇幅,针对数组的特化版本就不赘述了)。(代码中保留了 Dev C++ 的注释)

  /// Primary template of default_delete, used by unique_ptr
  template<typename _Tp>
  struct default_delete
  {
    /// Default constructor
    constexpr default_delete() noexcept = default;                   (1)

    /** @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                       (2)
        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                                (3)
    {
      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;
    }
  };

(1) deleter 的默认构造函数, 如果 unique_ptr 的构造器没有传入 deleter 对象作为参数,需要在其内部默认构造一个 deleter 对象。
(2) 拷贝构造函数。由于没有加 explicit 限定符, 因此可以用做隐式转换。由于 default_deleter 只是一个 function object,没有数据成员,因此实际上拷贝操作为空。
(3)该 funtion object 的主体,主要任务就是 delete 掉传入的 __ptr

std::is_convertible 判断类型之间是否能成功转换的模板。

  /// is_convertible
  template<typename _From, typename _To>                         (1)
  struct is_convertible
    :public __is_convertible_helper<_From, _To>::type { };

  template<typename _From, typename _To,                         (2)
     bool = __or_<is_void<_From>, is_function<_To>, is_array<_To>>::value>
  struct __is_convertible_helper
  { typedef typename is_void<_To>::type type; };

  template<typename _From, typename _To>
  class __is_convertible_helper<_From, _To, false>               (3)
  {
    template<typename _To1>static void __test_aux(_To1);         (3.1)

    template<typename _From1, typename _To1,                     (3.2)
    typename = decltype(__test_aux<_To1>(std::declval<_From1>()))>
    static true_type __test(int);                                

    template<typename, typename>                                 (3.3)
    static false_type __test(...);

 public:
   typedef decltype(__test<_From, _To>(0)) type;                 (3.4)
 };

(1) 判断类型之间是否能成功转换的模板。实际上 is_convertible 只是一个对外的口类, 类型转换的识别主要由其父类 __is_convertible_helper 完成。
(2) 限于篇幅,这里就不对 __or_, is_voidis_array 的源码进行分析了。从顾名思义, 在模板参数列表内判断 _From 是不是 void 类型, _To 是不是函数类型, 或者 _To 是不是数组类型。如果 _Fromvoid 类型,则 _To 只能为 void 类型才能转换。 如果 _To 是函数类型或者数组类型则不能实现转换。(type 类型是 true_typefalse_type 类似,用于标识是否能完成转换。)
(3) 是 (2) 的特化版本。当 (2) 的默认模板参数识别出 false_type (该类的对象能隐式转换为 false, 有机会我会细讲其源码。), 就会进入 (3) 的特化版本。 其实 (2) 是针对特殊情况(_Fromvoid 类型, _To 是函数类型, 或 _To 是数组类型)的, 而 (3) 才是针对一般类似是否能实现转换的判断。
利用编译器所谓的 SFINAE(Substitution Failure Is Not An Error) 技术, 实现对类型转换成功与否的判断。SFINAE, 即非模板函数具有最高优先权, 如果不存在匹配的非模板函数的话, 那么最匹配的和最特化的具有最高的优先权。
(3.1) 辅助函数声明。主要是利用静态模板函数声明, 实现在编译期获得其返回值类型(void的类型) 。 decltype(__test_aux<_To1>(std::declval<_From1>())) 就是利用 decltype 函数实现获取其返回值类型(void 类型)。当然,这里要求 _From1_To1 类型一致(模板函数的使用…), 如果不一致,则编译不通过,根据 SFINAE 查找下一个匹配的模板。
(3.2) 当传入第三个模板参数默认为 decltype(__test_aux<_To1>(std::declval<_From1>())), 当 _From1_To1 类型一致,则该参数为 void 类型,如果不一致,则编译不通过,根据 SFINAE 查找下一个匹配的模板。
(3.3) 任意函数参数的两个模板参数的模板函数。
(3.4) 链接 (3.2) 和 (3.3) 的中枢。将 type 定义为 __test<_From, _To>(0) 的类型, 根据模板推演原则, 首先选择需要一个函数参数 __test函数的模板函数, 即 (3.2)。但是, (3.2) 能成功推导的前提是 “_From1_To1 类型一致”。 如果一致,则type 被定义为 true_type 类型((3.2) 的返回值的类型)。 如果不一致, 则根据 SFINAE, 选择下一个较次的匹配项进行模板推演,即(3.3), 则定义 typefalse_type类型((3.3)的返回值类型)。

2. 辅助类 _Pointer 的源码(Dev C++ 实现的成员类, 非标准)

  template <typename _Tp, typename _Dp = default_delete<_Tp> >         (0)
  class unique_ptr;

  // use SFINAE to determine whether _Del::pointer exists
  class _Pointer
  {
    template<typename _Up>                                             (1)
    static typename _Up::pointer __test(typename _Up::pointer*);

    template<typename _Up>                                             (2)
      static _Tp* __test(...);

    typedef typename remove_reference<_Dp>::type _Del;                 (3)

  public:
    typedef decltype(__test<_Del>(0)) type;                            (4)
  };

(0) Dev C++ 中 unique_ptr 的声明。
(1) 如果 _Up::pointer 不存在, 则该声明不会被推导。
(2) 与 (1) 利用 SFINAE 一起判断 _Up::pointer 是否存在。
(3) 将移除引用的 _Dp 类型定义为 _Del
(4) 判断 _Del::pointer 是否存在的中枢。将 __test<_Del>(0)的返回值类型定义为 type。 根据模板推导原则, 首先选择含有一个函数参数的模板 (1), 如果 _Up::pointer 存在,则推导成功, 将 type 定义为 _Up::pointer 类型((1) 的返回值类型); 如果 _Up::pointer 不存在,根据 SFINAE, 编译器会接着选择较次的匹配, 即(2), 将 type 定义为 _Tp*((2)的返回值类型)。

题外话: 为什么不直接让 把 type 设为 _Tp* 呢?
答: 因为可能管理该资源的不是原始的指针, 而是用户定义的类似于指针的东西。如, 智能指针。

3. unique_ptr 的成员变量和成员类型

  private:
    typedef std::tuple<typename _Pointer::type, _Dp>  __tuple_type;      (1)
    __tuple_type                                      _M_t;

  public:
    typedef typename _Pointer::type   pointer;                           (2)
    typedef _Tp                       element_type;                      (3)
    typedef _Dp                       deleter_type;                      (4)

(1) 定义 tuple 的二元组类 __tuple_type, 存放所管理的资源的指针以及 deleter 对象。 并定义其对象 _M_t
(2) 所管理资源的指针的类型。
(3) 所管理资源的了类型。
(4) 负责销毁资源的 deleter 的类型。

题外话: 多元组 std::tuple 是用来存放任意数量不同类型的数据的集合。可以通过 get<i>(tup) 来获取 tup 的第 i 个元素。关于它的源码, 希望后续有时间能专门写博客来详细解读。

4. unique_ptr 构造函数的源码

  // Constructors.

  /// Default constructor, creates a unique_ptr that owns nothing.
  constexpr unique_ptr()                                       (1)
    : _M_t(){}

  /** Takes ownership of a pointer.
   *
   * @param __p  A pointer to an object of @c element_type
   *
   * The deleter will be value-initialized.
   */
  explicit unique_ptr(pointer __p)                             (2)
    : _M_t(__p, deleter_type()) { }

  /** 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,                                      (3)
    typename conditional<is_reference<deleter_type>::value,
    deleter_type, const deleter_type&>::type __d) 
    : _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,                                      (4)
    typename remove_reference<deleter_type>::type&& __d)
    : _M_t(std::move(__p), std::move(__d)) {  }

  /// Creates a unique_ptr that owns nothing.
  constexpr unique_ptr(nullptr_t) : unique_ptr() { }           (5)

  // Move constructors.

  /// Move constructor.
  unique_ptr(unique_ptr&& __u)                                 (6)
    : _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,                          (7)
    typename = _Require<is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>, __not_<is_array<_Up>>,
    typename conditional<is_reference<_Dp>::value, is_same<_Ep, _Dp>,
    is_convertible<_Ep, _Dp>>::type>>
    unique_ptr(unique_ptr<_Up, _Ep>&& __u)
    : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
    { }

#if _GLIBCXX_USE_DEPRECATED
  /// Converting constructor from @c auto_ptr
  template<typename _Up,                                       (8)
    typename = _Require<is_convertible<_Up*, _Tp*>, is_same<_Dp, default_delete<_Tp>>>>
    unique_ptr(auto_ptr<_Up>&& __u);
#endif

(1) 默认构造函数。只是创建 unique_ptr 对象, 并没有管理资源。
(2) 构造管理 __p 指向的资源的构造函数。 并将其 deleter 设为 deleter_type 默认构造的 deleter。
(3) 这个构造函数的声明在 unique_ptr 的使用部分已经介绍过了。构造管理指针 __p 指向的资源, 并且设置其 deleter 为 __d 的构造函数。
(4) 这个构造函数的声明也在 unique_ptr 的使用部分已经介绍过了。 与 (3) 类似,不过是针对 __d 是右值引用的情况。关于 std::move的相关知识,以后有机会细讲。
(5) 构造不管理任何资源的 unique_ptr 对象。
(6) 移动构造函数。与 auto_ptr 类似, unique_ptr 不与他人共同管理资源。实际上只需要实现其移动构造函数即可。std::forward 使其参数按照原来的值类型传递。
(7) 用满足要求的能转换为 *this 管理类型的资源的 __u, 移动构造或隐式转换为 *this
(8) 如果还有 auto_ptr 可用的话, 就加上从 auto_ptrunique_ptr 的移动构造(隐式转换)函数。

5. unique_ptr 析构函数

  /// Destructor, invokes the deleter if the stored pointer is not null.
  ~unique_ptr()
  {
    auto& __ptr = std::get<0>(_M_t);                 (1)

    if (__ptr != nullptr)                            (2)
      get_deleter()(__ptr);

    __ptr = pointer();                               (3)
  }

(1) _M_t 内依次是 *this 管理的资源和绑定的 deleter。所以 ptr实际上获得的是 *this 管理资源的指针的引用。
(2) 如果 __ptr != nullptr, 即 *this 有管理的对象, 则用 deleter 将该资源销毁。
(3) 重置管理指针。

6. unique_ptr 移动赋值函数

  // 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)                         (1)
  {
    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>                             (2)
    typename enable_if< __and_<
    is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
    __not_<is_array<_Up>>
    >::value,
      unique_ptr&>::type operator=(unique_ptr<_Up, _Ep>&& __u)
  {
    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)                                 (3)
  {
    reset();
    return *this;
  }

(1) 移动赋值函数。相较于 auto_ptr 复杂的实现, 有移动语义的 unique_ptr 的赋值函数更加简洁。 为了防止自我赋值产生奇怪错误, 这里用 __u.release() 函数返回控制资源的指针, 然后 reset*this
(2) 针对可转化为 *thisunique_ptr 的重载版本, 实现细节与 (1) 类似。
(3) 用 nullptr 赋值给 *this。 实际上就是将 *this 管理的资源清空。

7. unique_ptr 其他函数

  /// Dereference the stored pointer.
  typename add_lvalue_reference<element_type>::type operator*() const    (1)
  {
    return *get();
  }
  /// Return the stored pointer.
  pointer operator->() const                                             (2)
  {
    return get();
  }

  /// Return the stored pointer.
  pointer get() const                                                    (3)
  { return std::get<0>(_M_t); }
  /// Return a reference to the stored deleter.
  deleter_type& get_deleter()                                            (4)
  { return std::get<1>(_M_t); }
  /// Return a reference to the stored deleter.
  const deleter_type& get_deleter() const                                (4)
  { return std::get<1>(_M_t); }

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

  /// Release ownership of any stored pointer.
  pointer release()                                                      (6)
  {
    pointer __p = get();
    std::get<0>(_M_t) = 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())                                    (7)
  {
    using std::swap;
    swap(std::get<0>(_M_t), __p);
    if (__p != pointer())
      get_deleter()(__p);
  }

  /// Exchange the pointer and deleter with another object.
  void swap(unique_ptr& __u)                                            (8)
  {
    using std::swap;
    swap(_M_t, __u._M_t);
  }

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

(1) 重载 operator*()。通过 get() 函数获取 *this 管理的资源的引用。
(2) 重载 operator->()。通过 get() 函数获取 *this 管理的资源的指针。
(3) 从二元组 std::tuple<typename _Pointer::type, _Dp> 中获取 *this 管理的资源的指针。
(4) 从二元组 std::tuple<typename _Pointer::type, _Dp> 中获取 *this 绑定的 deleter 的 (常) 引用。
(5) 隐式转换为布尔类型的函数。前面有解释过 explicit 的作用。用于判断 *this 是否有管理的资源。
(6) 移交出 *this 管理的资源。 先让 __p 保存 *this 管理的资源, 然后让 *this 的 管理指针置零, 最后返回 __p
(7) 更改 *this 管理的资源。为了防止 __p == this 时错误的销毁了该资源, 用 swap 函数交换资源, 然后用 deleter 删除交换给原始指针的资源。
(8) 交换两个 unique_ptr 所管理的资源。 实际上就是交换其二元组 std::tuple<typename _Pointer::type, _Dp>
(9) 由于 unique_ptr 要求必须单独管理资源, 不能同时管理资源, 因此拷贝赋值函数实际上是没有意义的, 因此需要删除。

三. 总结

unique_ptr 的功能与 auto_ptr 类似, 都是按照 RAII 原则, 实现单独对资源的管理。 相较于 auto_ptr, unique_ptr 更进一步的是: unique_ptr 有右值引用以及强大的C++新特性的加持, 因此在很多实现上更加的完美; unique_ptr 可以设置销毁资源的 deleter, 这就使得它不仅可以管理内存资源, 也可以管理其它的需要释放的资源(需要设置对应的deleter)。

四. 参考文献

  • cppreference.com
  • Lippman 等著, 王刚等译《C++ Primer(第五版)》
  • Dec C++ 源码

五. 推荐阅读

猜你喜欢

转载自blog.csdn.net/yanglingwell/article/details/56682591