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 的声明

  1. // since C++11
  2. template<class T, class Deleter = std::default_delete<T>> (1)
  3. class unique_ptr;
  4. template<class T, class Deleter> (2)
  5. class unique_ptr<T[], Deleter>;

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

2. unique_ptr 的构造函数

  • unique_ptr<T> 的构造函数。
  1. constexpr unique_ptr(); (1)
  2. constexpr unique_ptr(nullptr_t);
  3. explicit unique_ptr(pointer p); (2)
  4. unique_ptr(pointer p, (3)
  5. typename conditional<is_reference<Deleter>::value,
  6. Deleter, const Deleter&>::type d);
  7. unique_ptr(pointer p, (4)
  8. typename remove_reference<Deleter>::type&& d);
  9. unique_ptr(unique_ptr&& u); (5)
  10. template<class U, class E> (6)
  11. unique_ptr(unique_ptr<U, E>&& u);
  12. template<class U> (7)
  13. 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 时,

    1. unique_ptr(pointer p, const A& d); (3)
    2. unique_ptr(pointer p, A&& d); (4)

    b. 当 Deleter 是左值引用类型 A& 时,

    1. unique_ptr(pointer p, A& d); (3)
    2. unique_ptr(pointer p, A&& d); (4)

    c. 当 Deleter 是 常左值引用类型 const A&时,

    1. unique_ptr(pointer p, const A& d); (3)
    2. 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)

  1. #include <iostream>
  2. #include <memory>
  3. struct Foo { // object to manage
  4. Foo() { std::cout << "Foo ctor\n"; }
  5. Foo(const Foo&) { std::cout << "Foo copy ctor\n"; }
  6. Foo(Foo&&) { std::cout << "Foo move ctor\n"; }
  7. ~Foo() { std::cout << "~Foo dtor\n"; }
  8. };
  9. struct D { // deleter
  10. D() {};
  11. D(const D&) { std::cout << "D copy ctor\n"; }
  12. D(D&) { std::cout << "D non-const copy ctor\n";}
  13. D(D&&) { std::cout << "D move ctor \n"; }
  14. void operator()(Foo* p) const {
  15. std::cout << "D is deleting a Foo\n";
  16. delete p;
  17. };
  18. };
  19. int main()
  20. {
  21. std::cout << "Example constructor(1)...\n";
  22. std::unique_ptr<Foo> up1; // up1 is empty
  23. std::unique_ptr<Foo> up1b(nullptr); // up1b is empty
  24. std::cout << "\nExample constructor(2)...\n";
  25. {
  26. std::unique_ptr<Foo> up2(new Foo); //up2 now owns a Foo
  27. } // Foo deleted
  28. std::cout << "\nExample constructor(3)...\n";
  29. D d;
  30. { // deleter type is not a reference
  31. std::unique_ptr<Foo, D> up3(new Foo, d); // deleter copied
  32. }
  33. { // deleter type is a reference
  34. std::unique_ptr<Foo, D&> up3b(new Foo, d); // up3b holds a reference to d
  35. }
  36. std::cout << "\nExample constructor(4)...\n";
  37. { // deleter is not a reference
  38. std::unique_ptr<Foo, D> up4(new Foo, D()); // deleter moved
  39. }
  40. std::cout << "\nExample constructor(5)...\n";
  41. {
  42. std::unique_ptr<Foo> up5a(new Foo);
  43. std::unique_ptr<Foo> up5b(std::move(up5a)); // ownership transfer
  44. }
  45. std::cout << "\nExample constructor(6)...\n";
  46. {
  47. std::unique_ptr<Foo, D> up6a(new Foo, d); // D is copied
  48. std::unique_ptr<Foo, D> up6b(std::move(up6a)); // D is moved
  49. std::unique_ptr<Foo, D&> up6c(new Foo, d); // D is a reference
  50. std::unique_ptr<Foo, D> up6d(std::move(up6c)); // D is copied
  51. }
  52. std::cout << "\nExample constructor(7)...\n";
  53. {
  54. std::auto_ptr<Foo> up7a(new Foo);
  55. std::unique_ptr<Foo> up7b(std::move(up7a)); // ownership transfer
  56. }
  57. }

执行结果:

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

  1. ~unique_ptr();

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

4. 拷贝赋值函数

  • unique_ptr<T> 的拷贝赋值函数。
  1. unique_ptr& opertor=(unique_ptr&& r); (1)
  2. template<class U, class E>
  3. unique_ptr& operator=(unique_ptr<U, E>&& r); (2)
  4. 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)

  1. #include <iostream>
  2. #include <memory>
  3. struct Foo {
  4. Foo() { std::cout << "Foo\n"; }
  5. ~Foo() { std::cout << "~Foo\n"; }
  6. };
  7. int main()
  8. {
  9. std::unique_ptr<Foo> p1;
  10. {
  11. std::cout << "Creating new Foo...\n";
  12. std::unique_ptr<Foo> p2(new Foo);
  13. // p1 = p2; // Error ! can't copy unique_ptr
  14. //unique_ptr& opertor=(unique_ptr&& r);
  15. p1 = std::move(p2);
  16. std::cout << "About to leave inner block...\n";
  17. // Foo instance will continue to live,
  18. // despite p2 going out of scope
  19. }
  20. // unique_ptr& operator=(nullptr_t);
  21. std::cout << "Creating new Foo...\n";
  22. std::unique_ptr<Foo> p3(new Foo);
  23. std::cout << "Before p3 = nullptr...\n";
  24. p3 = nullptr;
  25. std::cout << "After p3 = nullptr...\n";
  26. std::cout << "About to leave program...\n";
  27. }

执行结果:

  1. Creating new Foo...
  2. Foo
  3. About to leave inner block...
  4. Creating new Foo...
  5. Foo
  6. Before p3 = nullptr...
  7. ~Foo
  8. After p3 = nullptr...
  9. About to leave program...
  10. ~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> 与之类似。

  1. pointer release(); (1)
  2. void reset(pointer ptr = pointer()); (2)
  3. void swap(unique_ptr& other); (3)
  4. pointer get() const; (4)
  5. Deleter& get_deleter(); (5)
  6. const Deleter& get_deleter() const; (6)
  7. explicit operator bool() const; (7)
  8. typename std::add_lvalue_reference<T>::type (8)
  9. operator*() const;
  10. 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。例如:

  1. struct A
  2. {
  3. operator bool() const { return true; }
  4. };
  5. struct B
  6. {
  7. explicit operator bool() const { return true;
  8. };
  9. int main()
  10. {
  11. A a1;
  12. bool na1 = a1; // OK: copy-initialization selects A::operator bool()
  13. bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization
  14. B b2;
  15. if (b2) ; // OK: B::operator bool()
  16. // bool nb1 = b2; // error: copy-initialization does not consider B::operator bool()
  17. bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization
  18. }

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

  • 例子
  1. #include <iostream>
  2. #include <memory>
  3. #include <string>
  4. using namespace std;
  5. struct Foo {
  6. Foo(string name = "Foo") : m_name(name)
  7. { std::cout << m_name << " is creating...\n"; }
  8. ~Foo() { std::cout << m_name << " is deleting...\n"; }
  9. string m_name;
  10. };
  11. class FooDeleter{
  12. public:
  13. void Show() const { cout << "I'm FooDeleter..." << endl; }
  14. void operator()(Foo* pf) const{
  15. cout << "FooDeleter is deleting " << pf->m_name << endl;
  16. delete pf;
  17. }
  18. };
  19. int main()
  20. {
  21. {
  22. // for realese()...
  23. cout << "test for release()..." << endl << endl;
  24. unique_ptr<Foo, FooDeleter> f1(new Foo("Foo f1"));
  25. Foo* pf1 = f1.release(); // 将资源的管理权交给 pf1
  26. cout << "before delete pf1..." << endl;
  27. delete pf1;
  28. cout << "after delete pf1..." << endl << endl;
  29. }
  30. cout << "--------------------------------" << endl << endl;
  31. {
  32. // for reset()...
  33. cout << "test for reset()..." << endl << endl;
  34. unique_ptr<Foo, FooDeleter> f2(new Foo("Foo f2"));
  35. Foo* pf2 = new Foo("Foo pf2");
  36. cout << "before f2.reset(pf2)..." << endl;
  37. f2.reset(pf2);
  38. cout << "after f2.reset(pf2)..." << endl << endl;
  39. }
  40. cout << "--------------------------------" << endl << endl;
  41. {
  42. // for swap()...
  43. cout << "test for swap()..." << endl << endl;
  44. unique_ptr<Foo, FooDeleter> f3(new Foo("Foo f3"));
  45. unique_ptr<Foo, FooDeleter> f4(new Foo("Foo f4"), FooDeleter());
  46. f3.swap(f4);
  47. cout << "before f3.reset(nullptr)..." << endl;
  48. f3.reset(nullptr);
  49. cout << "after f3.reset(nullptr)..." << endl << endl;
  50. cout << "before f4.reset(nullptr)..." << endl;
  51. f4.reset(nullptr);
  52. cout << "after f4.reset(nullptr)..." << endl << endl;
  53. }
  54. cout << "--------------------------------" << endl << endl;
  55. {
  56. // for get(), get_deleter(), operator bool(), operator*() and operator->()...
  57. cout << "test for get(), get_deleter(), operator bool()" << endl
  58. << "operator*() and operator->()..." << endl << endl;
  59. unique_ptr<Foo, FooDeleter> f5(new Foo("Foo f5"));
  60. unique_ptr<Foo, FooDeleter> defaultF;
  61. // for get()...
  62. Foo* pf3 = f5.get();
  63. cout << " f5.get()->m_name = " << pf3->m_name << endl;
  64. // for get_deleter()...
  65. FooDeleter& pd1 = f5.get_deleter();
  66. pd1.Show();
  67. // for operator bool()
  68. if(f5) cout << "f5 == true" << endl;
  69. if(!defaultF) cout << "defaultF == false" << endl;
  70. // for operator*() and operator->()...
  71. cout << "f5->m_name == " << f5->m_name << endl;
  72. cout << "(*f5).m_name == " << (*f5).m_name << endl;
  73. }
  74. cout << "--------------------------------" << endl << endl;
  75. }

运行结果:

  1. test for release()...
  2. Foo f1 is creating...
  3. before delete pf1...
  4. Foo f1 is deleting...
  5. after delete pf1...
  6. --------------------------------
  7. test for reset()...
  8. Foo f2 is creating...
  9. Foo pf2 is creating...
  10. before f2.reset(pf2)...
  11. FooDeleter is deleting Foo f2
  12. Foo f2 is deleting...
  13. after f2.reset(pf2)...
  14. FooDeleter is deleting Foo pf2
  15. Foo pf2 is deleting...
  16. --------------------------------
  17. test for swap()...
  18. Foo f3 is creating...
  19. Foo f4 is creating...
  20. before f3.reset(nullptr)...
  21. FooDeleter is deleting Foo f4
  22. Foo f4 is deleting...
  23. after f3.reset(nullptr)...
  24. before f4.reset(nullptr)...
  25. FooDeleter is deleting Foo f3
  26. Foo f3 is deleting...
  27. after f4.reset(nullptr)...
  28. --------------------------------
  29. test for get(), get_deleter(), operator bool()
  30. operator*() and operator->()...
  31. Foo f5 is creating...
  32. f5.get()->m_name = Foo f5
  33. I'm FooDeleter...
  34. f5 == true
  35. defaultF == false
  36. f5->m_name == Foo f5
  37. (*f5).m_name == Foo f5
  38. FooDeleter is deleting Foo f5
  39. Foo f5 is deleting...
  40. --------------------------------

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

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

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

  1. /// Primary template of default_delete, used by unique_ptr
  2. template<typename _Tp>
  3. struct default_delete
  4. {
  5. /// Default constructor
  6. constexpr default_delete() noexcept = default; (1)
  7. /** @brief Converting constructor.
  8. *
  9. * Allows conversion from a deleter for arrays of another type, @p _Up,
  10. * only if @p _Up* is convertible to @p _Tp*.
  11. */
  12. template<typename _Up, typename = typename (2)
  13. enable_if<is_convertible<_Up*, _Tp*>::value>::type>
  14. default_delete(const default_delete<_Up>&) noexcept { }
  15. /// Calls @c delete @p __ptr
  16. void operator()(_Tp* __ptr) const (3)
  17. {
  18. static_assert(!is_void<_Tp>::value, "can't delete pointer to incomplete type");
  19. static_assert(sizeof(_Tp)>0, "can't delete pointer to incomplete type");
  20. delete __ptr;
  21. }
  22. };

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

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

  1. /// is_convertible
  2. template<typename _From, typename _To> (1)
  3. struct is_convertible
  4. :public __is_convertible_helper<_From, _To>::type { };
  5. template<typename _From, typename _To, (2)
  6. bool = __or_<is_void<_From>, is_function<_To>, is_array<_To>>::value>
  7. struct __is_convertible_helper
  8. { typedef typename is_void<_To>::type type; };
  9. template<typename _From, typename _To>
  10. class __is_convertible_helper<_From, _To, false> (3)
  11. {
  12. template<typename _To1>static void __test_aux(_To1); (3.1)
  13. template<typename _From1, typename _To1, (3.2)
  14. typename = decltype(__test_aux<_To1>(std::declval<_From1>()))>
  15. static true_type __test(int);
  16. template<typename, typename> (3.3)
  17. static false_type __test(...);
  18. public:
  19. typedef decltype(__test<_From, _To>(0)) type; (3.4)
  20. };

(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++ 实现的成员类, 非标准)

  1. template <typename _Tp, typename _Dp = default_delete<_Tp> > (0)
  2. class unique_ptr;
  3. // use SFINAE to determine whether _Del::pointer exists
  4. class _Pointer
  5. {
  6. template<typename _Up> (1)
  7. static typename _Up::pointer __test(typename _Up::pointer*);
  8. template<typename _Up> (2)
  9. static _Tp* __test(...);
  10. typedef typename remove_reference<_Dp>::type _Del; (3)
  11. public:
  12. typedef decltype(__test<_Del>(0)) type; (4)
  13. };

(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 的成员变量和成员类型

  1. private:
  2. typedef std::tuple<typename _Pointer::type, _Dp> __tuple_type; (1)
  3. __tuple_type _M_t;
  4. public:
  5. typedef typename _Pointer::type pointer; (2)
  6. typedef _Tp element_type; (3)
  7. 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 构造函数的源码

  1. // Constructors.
  2. /// Default constructor, creates a unique_ptr that owns nothing.
  3. constexpr unique_ptr() (1)
  4. : _M_t(){}
  5. /** Takes ownership of a pointer.
  6. *
  7. * @param __p A pointer to an object of @c element_type
  8. *
  9. * The deleter will be value-initialized.
  10. */
  11. explicit unique_ptr(pointer __p) (2)
  12. : _M_t(__p, deleter_type()) { }
  13. /** Takes ownership of a pointer.
  14. *
  15. * @param __p A pointer to an object of @c element_type
  16. * @param __d A reference to a deleter.
  17. *
  18. * The deleter will be initialized with @p __d
  19. */
  20. unique_ptr(pointer __p, (3)
  21. typename conditional<is_reference<deleter_type>::value,
  22. deleter_type, const deleter_type&>::type __d)
  23. : _M_t(__p, __d) { }
  24. /** Takes ownership of a pointer.
  25. *
  26. * @param __p A pointer to an object of @c element_type
  27. * @param __d An rvalue reference to a deleter.
  28. *
  29. * The deleter will be initialized with @p std::move(__d)
  30. */
  31. unique_ptr(pointer __p, (4)
  32. typename remove_reference<deleter_type>::type&& __d)
  33. : _M_t(std::move(__p), std::move(__d)) { }
  34. /// Creates a unique_ptr that owns nothing.
  35. constexpr unique_ptr(nullptr_t) : unique_ptr() { } (5)
  36. // Move constructors.
  37. /// Move constructor.
  38. unique_ptr(unique_ptr&& __u) (6)
  39. : _M_t(__u.release(),
  40. std::forward<deleter_type>(__u.get_deleter())) { }
  41. /** @brief Converting constructor from another type
  42. *
  43. * Requires that the pointer owned by @p __u is convertible to the
  44. * type of pointer owned by this object, @p __u does not own an array,
  45. * and @p __u has a compatible deleter type.
  46. */
  47. template<typename _Up, typename _Ep, (7)
  48. typename = _Require<is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>, __not_<is_array<_Up>>,
  49. typename conditional<is_reference<_Dp>::value, is_same<_Ep, _Dp>,
  50. is_convertible<_Ep, _Dp>>::type>>
  51. unique_ptr(unique_ptr<_Up, _Ep>&& __u)
  52. : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
  53. { }
  54. #if _GLIBCXX_USE_DEPRECATED
  55. /// Converting constructor from @c auto_ptr
  56. template<typename _Up, (8)
  57. typename = _Require<is_convertible<_Up*, _Tp*>, is_same<_Dp, default_delete<_Tp>>>>
  58. unique_ptr(auto_ptr<_Up>&& __u);
  59. #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 析构函数

  1. /// Destructor, invokes the deleter if the stored pointer is not null.
  2. ~unique_ptr()
  3. {
  4. auto& __ptr = std::get<0>(_M_t); (1)
  5. if (__ptr != nullptr) (2)
  6. get_deleter()(__ptr);
  7. __ptr = pointer(); (3)
  8. }

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

6. unique_ptr 移动赋值函数

  1. // Assignment.
  2. /** @brief Move assignment operator.
  3. *
  4. * @param __u The object to transfer ownership from.
  5. *
  6. * Invokes the deleter first if this object owns a pointer.
  7. */
  8. unique_ptr& operator=(unique_ptr&& __u) (1)
  9. {
  10. reset(__u.release());
  11. get_deleter() = std::forward<deleter_type>(__u.get_deleter());
  12. return *this;
  13. }
  14. /** @brief Assignment from another type.
  15. *
  16. * @param __u The object to transfer ownership from, which owns a
  17. * convertible pointer to a non-array object.
  18. *
  19. * Invokes the deleter first if this object owns a pointer.
  20. */
  21. template<typename _Up, typename _Ep> (2)
  22. typename enable_if< __and_<
  23. is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
  24. __not_<is_array<_Up>>
  25. >::value,
  26. unique_ptr&>::type operator=(unique_ptr<_Up, _Ep>&& __u)
  27. {
  28. reset(__u.release());
  29. get_deleter() = std::forward<_Ep>(__u.get_deleter());
  30. return *this;
  31. }
  32. /// Reset the %unique_ptr to empty,invoking the deleter if necessary.
  33. unique_ptr& operator=(nullptr_t) (3)
  34. {
  35. reset();
  36. return *this;
  37. }

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

7. unique_ptr 其他函数

  1. /// Dereference the stored pointer.
  2. typename add_lvalue_reference<element_type>::type operator*() const (1)
  3. {
  4. return *get();
  5. }
  6. /// Return the stored pointer.
  7. pointer operator->() const (2)
  8. {
  9. return get();
  10. }
  11. /// Return the stored pointer.
  12. pointer get() const (3)
  13. { return std::get<0>(_M_t); }
  14. /// Return a reference to the stored deleter.
  15. deleter_type& get_deleter() (4)
  16. { return std::get<1>(_M_t); }
  17. /// Return a reference to the stored deleter.
  18. const deleter_type& get_deleter() const (4)
  19. { return std::get<1>(_M_t); }
  20. /// Return @c true if the stored pointer is not null.
  21. explicit operator bool() const (5)
  22. { return get() == pointer() ? false : true; }
  23. /// Release ownership of any stored pointer.
  24. pointer release() (6)
  25. {
  26. pointer __p = get();
  27. std::get<0>(_M_t) = pointer();
  28. return __p;
  29. }
  30. /** @brief Replace the stored pointer.
  31. *
  32. * @param __p The new pointer to store.
  33. *
  34. * The deleter will be invoked if a pointer is already owned.
  35. */
  36. void reset(pointer __p = pointer()) (7)
  37. {
  38. using std::swap;
  39. swap(std::get<0>(_M_t), __p);
  40. if (__p != pointer())
  41. get_deleter()(__p);
  42. }
  43. /// Exchange the pointer and deleter with another object.
  44. void swap(unique_ptr& __u) (8)
  45. {
  46. using std::swap;
  47. swap(_M_t, __u._M_t);
  48. }
  49. // Disable copy from lvalue.
  50. unique_ptr(const unique_ptr&) = delete; (9)
  51. 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++ 源码

五. 推荐阅读

STL中的智能指针(Smart Pointer)及其源码剖析: std::unique_ptr的更多相关文章

  1. c/c++ 标准库 智能指针( smart pointer ) 是啥玩意儿

    标准库 智能指针( smart pointer ) 是啥玩意儿 一,为什么有智能指针??? c++程序员需要自己善后自己动态开辟的内存,一旦忘了释放,内存就泄露. 智能指针可以帮助程序员"自 ...

  2. (转)Delphi2009初体验 - 语言篇 - 智能指针(Smart Pointer)的实现

     转载:http://www.cnblogs.com/felixYeou/archive/2008/08/27/1277250.html 快速导航 一. 回顾历史二. 智能指针简介三. Delphi中 ...

  3. 智能指针类模板(上)——STL中的智能指针

    智能指针类模板智能指针本质上就是一个对象,它可以像原生指针那样来使用. 智能指针的意义-现代C++开发库中最重要的类模板之一-C++中自动内存管理的主要手段-能够在很大程度上避开内存相关的问题 1.内 ...

  4. 转载:STL四种智能指针

    转载至:https://blog.csdn.net/K346K346/article/details/81478223 STL一共给我们提供了四种智能指针: auto_ptr.unique_ptr.s ...

  5. C++中的智能指针类模板

    1,智能指针本质上是一个对象,这个对象可以像原生的指针一样使用,因为智能指 针相关的类通过重载的技术将指针相关的操作符都进行了重载,所以智能指针对象可以像原生指针一样操作,今天学习智能指针类模板,通过 ...

  6. OSG中的智能指针

    在OpenSceneGraph中,智能指针(Smart pointer)的概念指的是一种类的模板,它针对某一特定类型的对象(即Referenced类及其派生类)构建,提供了自己的管理模式,以避免因为用 ...

  7. 标准库中的智能指针shared_ptr

    智能指针的出现是为了能够更加方便的解决动态内存的管理问题.注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的.这个是容易理解的,vec ...

  8. C++ 中的智能指针-基础

    简介 在现代 C++ 编程中,标准库包含了智能指针(Smart pointers). 智能指针用来确保程序不会出现内存和资源的泄漏,并且是"异常安全"(exception-safe ...

  9. RPCZ中的智能指针单例

    RPCZ中的智能指针单例 (金庆的专栏) 智能指针单例应用于 RPCZ 库以实现库的自动初始化与自动清理. RPCZ: RPC implementation for Protocol Buffers ...

  10. Boost中的智能指针(转)

    这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一 ...

随机推荐

  1. Xamarin.Android带参数返回上一级界面

    在ActivityA跳转到ActivityB后.activityB返回到ActivityA并带参数返回 首先再activitya中跳转到b var intent = new Intent(this, ...

  2. mysql-front连接mysql报错1251解决方法

    客户端不通过服务器的验证,建议升级mysql客户端 出现错误的原因是mysql版本问题,由于mysql8.0版本与mysql5.0版本加密方式不同,导致mysql-front无法通过验证. mysql ...

  3. 【sqoop】简介、原理、安装配置测试、导入导出案例、脚本打包、常见命令及参数介绍、常用命令举例

    一.sqoop简介 用于在Hadoop(Hive)与传统的数据库(mysql.oracle...)之间进行数据的传递,可以将一个关系型数据库(例如 : MySQL ,Oracle ,Postgres等 ...

  4. Redis的数据复制

    介绍 Redis 的复制 Redis 的复制功能分为同步(sync)和命令传播(command propagate)这两个操作 同步操作用于,将从服务器的数据库状态更新至主服务器当前所处的数据库状态: ...

  5. Python实验报告(第6章)

    实验6:函数 一.实验目的和要求 1.掌握函数的创建和调用: 2.了解不同的参数如何进行传递: 3.了解返回值的应用: 4.学习变量的作用域: 5.学习匿名函数(lambda). 二.实验环境 软件版 ...

  6. Visual Studio 2022 MAUI NU1105(NETSDK1005) 处理记录

    故障说明 MAUI项目是日常使用的项目,一直都好好的 某一天修改了几行代码后,突然项目无法编译了,提示NU1105错误 从Git重新拉取一份之前的代码编译也是同样的错误,经过半天的查阅,尝试了几种方案 ...

  7. 主题 1 The Shell

    主题 1 The Shell 课程概览与 shell · the missing semester of your cs education (missing-semester-cn.github.i ...

  8. C#开发的线程池和管理器 - 开源研究系列文章

    上次编写了一个小软件,用于练手及自己的一个小工具集合.今天把其中的线程池和管理器的代码抽取出来,写成一个博文,让需要的朋友能够进行学习和应用. 这个线程管理器包括了3个类库和一个应用程序,见下图: 第 ...

  9. Linux安装&卸载mysql5.7

    Linux系统下卸载mysql 停止mysql服务 systemctl stop mysqld.service 查看安装的mysql服务 rpm -qa|grep -i mysql 删除安装的mysq ...

  10. [UOJ96] 【集训队互测2015】胡策的小树

    先考虑不掺金坷垃的做法. 设猴子处于 \(i\) 节点的概率为 \(f_i\),列出方程如下(\(i\) 的祖先包括自身): \[f_i = \sum_{j为i祖先}\frac{1-p_j}{siz_ ...