转自:https://www.cnblogs.com/DswCnblog/p/5628195.html

成员函数

(1) get 获得内部对象的指针, 由于已经重载了()方法, 因此和直接使用对象是一样的.如 unique_ptr<int> sp(new int(1)); sp 与 sp.get()是等价的

(2) release            放弃内部对象的所有权,将内部指针置为空, 返回所内部对象的指针, 此指针需要手动释放

(3) reset              销毁内部对象并接受新的对象的所有权(如果使用缺省参数的话,也就是没有任何对象的所有权, 此时仅将内部对象释放, 并置为空)

(4) swap               交换两个 shared_ptr 对象(即交换所拥有的对象) std::move(up)      所有权转移(通过移动语义), up所有权转移后,变成“空指针” (up 的定义为 std::unique_ptr<Ty> up)

unique_ptr 不支持拷贝和赋值.

std::unique_ptr<A> up1(new A(5));   

std::unique_ptr<A> up2(up1);           // 错误, unique_ptr 不支持拷贝               

std::unique_ptr<A> up2 = up1;          // 错误, unique_ptr 不支持赋值

虽然 unique_ptr 不支持拷贝和赋值, 但是我们可以调用 release 或 reset 将指针的所有权从一个(非 const) unique_ptr 转移到另一个.   

std::unique_ptr<int> up1(new int(1));   

std::unique_ptr<int> up2(up1.release());

虽然 unique_ptr 不支持拷贝, 但是可以从函数中返回, 甚至返回局部对象. 如下面的代码, 编译器知道要返回的对象即将被销毁, 因此执行一种特殊的"拷贝":

  template <class Ty>   

  std::unique_ptr<Ty> Clone(const Ty& obj)   

  {     return std::unique_ptr<Ty>(new Ty(obj));   }
  template <class Ty>   

  std::unique_ptr<Ty> Clone(const Ty& obj)   

  {     std::unique_ptr<Ty> temp = std::unique_ptr<Ty>(new Ty(obj));     return temp;   }

unique_ptr 不共享它的指针。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法。只能移动unique_ptr。这意味着,内存资源所有权将转移到另一 unique_ptr,并且原始 unique_ptr 不再拥有此资源。我们建议你将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于纯 C++ 对象时,可使用 unique_ptr,而当构造 unique_ptr 时,可使用make_unique Helper 函数。

std::unique_ptr实现了独享所有权的语义。一个非空的std::unique_ptr总是拥有它所指向的资源。转移一个std::unique_ptr将会把所有权也从源指针转移给目标指针(源指针被置空)。拷贝一个std::unique_ptr将不被允许,因为如果你拷贝一个std::unique_ptr,那么拷贝结束后,这两个std::unique_ptr都会指向相同的资源,它们都认为自己拥有这块资源(所以都会企图释放)。因此std::unique_ptr是一个仅能移动(move_only)的类型。当指针析构时,它所拥有的资源也被销毁。默认情况下,资源的析构是伴随着调用std::unique_ptr内部的原始指针的delete操作的。

下图演示了两个 unique_ptr 实例之间的所有权转换。

1、如何创建unique_ptr

unique_ptr不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。要想创建一个unique_ptr,我们需要将一个new 操作符返回的指针传递给unique_ptr的构造函数。

示例:

  1. int main()
  2. {
  3. // 创建一个unique_ptr实例
  4. unique_ptr<int> pInt(new int(5));
  5. cout << *pInt;
  6. }

2、无法进行复制构造和赋值操作

unique_ptr没有copy构造函数,不支持普通的拷贝和赋值操作。

  1. int main()
  2. {
  3. // 创建一个unique_ptr实例
  4. unique_ptr<int> pInt(new int(5));
  5. unique_ptr<int> pInt2(pInt); // 报错
  6. unique_ptr<int> pInt3 = pInt; // 报错
  7. }

3、可以进行移动构造和移动赋值操作

unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。如果需要转移所有权,可以使用std::move()函数。

示例:

  1. int main()
  2. {
  3. unique_ptr<int> pInt(new int(5));
  4. unique_ptr<int> pInt2 = std::move(pInt); // 转移所有权
  5. //cout << *pInt << endl; // 出错,pInt为空
  6. cout << *pInt2 << endl;
  7. unique_ptr<int> pInt3(std::move(pInt2));
  8. }

4、可以返回unique_ptr

unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。

示例:

  1. unique_ptr<int> clone(int p)
  2. {
  3. unique_ptr<int> pInt(new int(p));
  4. return pInt; // 返回unique_ptr
  5. }
  6.  
  7. int main() {
  8. int p = 5;
  9. unique_ptr<int> ret = clone(p);
  10. cout << *ret << endl;
  11. }
  1. 使用举例
  2. {
  3. //创建一个指向int的空指针
  4. std::unique_ptr<int> fPtr1;
  5. std::unique_ptr<int> fPtr2(new int(4));
  6. auto fPtr3 = std::make_unique<int>();
  7.  
  8. //fPtr2释放指向对象的所有权,并且被置为nullptr
  9. std::cout << "fPtr2 release before:" << fPtr2.get() << std::endl;
  10. int *pF = fPtr2.release();
  11. std::cout << "fPtr2 release before:" << fPtr2.get() << " and pF value:" << *pF << std::endl;
  12.  
  13. //所有权转移,转移后fPtr3变为空指针
  14. std::cout << "move before fPtr1 address:" << fPtr1.get() << " fPtr3 address:" << fPtr3.get() << std::endl;
  15. fPtr1 = std::move(fPtr3);
  16. std::cout << "move after fPtr1 address:" << fPtr1.get() << " fPtr3 address:" << fPtr3.get() << std::endl;
  17.  
  18. std::cout << "move before fPtr1 address:" << fPtr1.get() << std::endl;
  19. fPtr1.reset();
  20. std::cout << "move after fPtr1 address:" << fPtr1.get() << std::endl;
  21. }
  22.  
  23. 输出:
  24.   fPtr2 release before:00EFB120
  25.   fPtr2 release before:00000000 and pF value:4
  26.   move before fPtr1 address:00000000 fPtr3 address:00EFEC60
  27.   move after fPtr1 address:00EFEC60 fPtr3 address:00000000
  28.   move before fPtr1 address:00EFEC60
  29.   move after fPtr1 address:

unique_ptr使用场景

1、为动态申请的资源提供异常安全保证

我们先来看看下面这一段代码:

  1. void Func()
  2. {
  3. int *p = new int(5);
  4.  
  5. // ...(可能会抛出异常)
  6.  
  7. delete p;
  8. }

这是我们传统的写法:当我们动态申请内存后,有可能我们接下来的代码由于抛出异常或者提前退出(if语句)而没有执行delete操作。

解决的方法是使用unique_ptr来管理动态内存,只要unique_ptr指针创建成功,其析构函数都会被调用。确保动态资源被释放。

  1. void Func()
  2. {
  3. unique_ptr<int> p(new int(5));
  4.  
  5. // ...(可能会抛出异常)
  6. }

2、返回函数内动态申请资源的所有权

  1. unique_ptr<int> Func(int p)
  2. {
  3. unique_ptr<int> pInt(new int(p));
  4. return pInt; // 返回unique_ptr
  5. }
  6.  
  7. int main() {
  8. int p = 5;
  9. unique_ptr<int> ret = Func(p);
  10. cout << *ret << endl;
  11. // 函数结束后,自动释放资源
  12. }

3、在容器中保存指针

  1. int main()
  2. {
  3. vector<unique_ptr<int>> vec;
  4. unique_ptr<int> p(new int(5));
  5. vec.push_back(std::move(p)); // 使用移动语义
  6. }

4、管理动态数组

标准库提供了一个可以管理动态数组的unique_ptr版本。

  1. int main()
  2. {
  3. unique_ptr<int[]> p(new int[5] {1, 2, 3, 4, 5});
  4. p[0] = 0; // 重载了operator[]
  5. }

5、作为auto_ptr的替代品

创建与释放举例

  1. #include <iostream>
  2. #include <memory>
  3. #include <stdlib.h>
  4.  
  5. struct Foo
  6. {
  7. Foo() { std::cout << "Foo::Foo\n"; }
  8. ~Foo() { std::cout << "Foo::~Foo\n"; }
  9. void bar() { std::cout << "Foo::bar\n"; }
  10. };
  11.  
  12. void f(const Foo &)
  13. {
  14. std::cout << "f(const Foo&)\n";
  15. }
  16.  
  17. struct D
  18. {
  19. void operator()(Foo* foo)
  20. {
  21. std::cout << "D operator()" << std::endl;
  22. delete foo;
  23. }
  24. };
  25.  
  26. void TestAutoDestroy()
  27. {
  28. //1. 普通的new对象.
  29. std::cout << "TestDestroy...................." << std::endl;
  30. {
  31. std::unique_ptr<Foo> p1(new Foo);
  32. }
  33. //2. 普通的new[]对象.
  34. {
  35. std::unique_ptr<Foo[]> p2(new Foo[4]);
  36. }
  37. //3. 自定义的deleter.
  38. {
  39. std::unique_ptr<Foo, D> p3(new Foo);
  40. }
  41. }
  42.  
  43. void TestOwner()
  44. {
  45. std::cout << "TestOwner...................." << std::endl;
  46. //1. new object.
  47. std::unique_ptr<Foo> p1(new Foo); // p1 owns Foo
  48. if (p1) p1->bar();
  49.  
  50. {
  51. std::unique_ptr<Foo> p2(std::move(p1)); // now p2 owns Foo
  52. f(*p2);
  53.  
  54. p1 = std::move(p2); // ownership returns to p1
  55. p2->bar();
  56. std::cout << "destroying p2...\n";
  57. }
  58.  
  59. p1->bar();
  60. }
  61.  
  62. void TestArrayOwner()
  63. {
  64. std::cout << "TestArrayOwner...................." << std::endl;
  65. //1. new[] object.
  66. std::unique_ptr<Foo[]> p1(new Foo[4]); // p1 owns Foo
  67. if (p1) p1[0].bar();
  68.  
  69. {
  70. std::unique_ptr<Foo[]> p2(std::move(p1)); // now p2 owns Foo
  71. f(p2[0]);
  72.  
  73. p1 = std::move(p2); // ownership returns to p1
  74. p2[0].bar();
  75. std::cout << "destroying p2...\n";
  76. }
  77.  
  78. p1[0].bar();
  79. }
  80.  
  81. int main()
  82. {
  83. TestAutoDestroy();
  84. TestOwner();
  85. TestArrayOwner();
  86. }
    输出:
  1. TestDestroy....................
  2. Foo::Foo
  3. Foo::~Foo
  4. Foo::Foo
  5. Foo::Foo
  6. Foo::Foo
  7. Foo::Foo
  8. Foo::~Foo
  9. Foo::~Foo
  10. Foo::~Foo
  11. Foo::~Foo
  12. Foo::Foo
  13. operator()
  14. Foo::~Foo
  15. TestOwner....................
  16. Foo::Foo
  17. Foo::bar
  18. f(const Foo&)
  19. Foo::bar
  20. destroying p2...
  21. Foo::bar
  22. Foo::~Foo
  23. TestArrayOwner....................
  24. Foo::Foo
  25. Foo::Foo
  26. Foo::Foo
  27. Foo::Foo
  28. Foo::bar
  29. f(const Foo&)
  30. Foo::bar
  31. destroying p2...
  32. Foo::bar
  33. Foo::~Foo
  34. Foo::~Foo
  35. Foo::~Foo
  36. Foo::~Foo
  1.  

一下原文:https://www.cnblogs.com/wangkeqin/p/9383658.html

一个unique_ptr"拥有“他所指向的对象。与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定的对象。当unique_ptr被销毁时,它所指向的对象也被销毁。uniptr_ptr表达的是一种独占的思想。

 

初始化

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. //常规操作
  5. int main(int argc, char *argv[])
  6. {
  7. unique_ptr<double> p1; //!可指向一个double的unique_ptr
  8. unique_ptr<int> p2(new int(56)); //!p2指向了一个值为42的int
  9. unique_ptr<string> pstr(new string("strtest"));
  10. // unique_ptr<string> pstrCopy(pstr); //!error: 不支持对象的拷贝
  11. unique_ptr<string> pstrAssin;
  12. // pstrAssin = pstr //!error: uniptr不支持赋值
  13. return 0;
  14. }

unique_ptr一般操作

  关于unique_ptr还支持哪些操作,在前面的博文中我也做了总结,请参考该篇文章中图表:https://www.cnblogs.com/wangkeqin/p/9351191.html

 unique_ptr所有权转移

  虽然我们不能拷贝赋值unique_ptr,但是可以通过调用release或者set将指针的所有权从一个(非const)unique_ptr转移给一个unique:

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. class TEST
  5. {
  6. public:
  7. TEST(const string & name)
  8. :_name(name)
  9. {cout<<"TEST:"<<_name<<endl;}
  10. TEST(const TEST & another)
  11. { _name = another._name;
  12. cout<<another._name<<" copyStruct "<<_name<<endl;}
  13. TEST & operator =(const TEST & another){
  14. if(&another==this)
  15. return *this;
  16. this->_name=another._name;
  17. cout<<another._name<<" copyAssin to "<<_name<<endl;
  18. }
  19. ~TEST(){cout<<"~TEST:"<<_name<<endl;}
  20. //private:
  21. string _name;
  22. };
  23. //其他操作
  24. int main()
  25. {
  26. unique_ptr<TEST> p1(new TEST("case_1"));
  27. unique_ptr<TEST> p2(p1.release()); //!将所有权从p1转移到p2,p1现在指向NULL。
  28. cout<<"++++++++++++++++++++++++"<<endl;
  29. unique_ptr<TEST> p3(new TEST("case_2"));
  30. p2.reset(p3.release()); //!p2释放了原来指向的内存,接受了p3指向的内存。
  31. getchar();
  32. }

传递unique_ptr参数和返回unique_ptr

  不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或者赋值一个将要被销毁的unique_ptr。其本质就是调用了移动拷贝和移动赋值;最常见的例子是从函数返回一个unique_ptr:

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. class TEST
  5. {
  6. public:
  7. TEST(const string & name)
  8. :_name(name)
  9. {cout<<"TEST:"<<_name<<endl;}
  10. TEST(const TEST & another)
  11. { _name = another._name;
  12. cout<<another._name<<" copyStruct "<<_name<<endl;}
  13. TEST & operator =(const TEST & another){
  14. if(&another==this)
  15. return *this;
  16. this->_name=another._name;
  17. cout<<another._name<<" copyAssin to "<<_name<<endl;
  18. }
  19. ~TEST(){cout<<"~TEST:"<<_name<<endl;}
  20. //private:
  21. string _name;
  22. };
  23. //!例外:
  24. //①返回一个即将被销毁的uniptr
  25. unique_ptr<TEST> retDying(string param)
  26. {
  27. return unique_ptr<TEST>(new TEST(param));
  28. }
  29. //②返回一个局部对象;
  30. unique_ptr<TEST> retTemp(string param)
  31. {
  32. unique_ptr<TEST> pTemp(new TEST(param));
  33. return pTemp;
  34. }
  35. int main()
  36. {
  37. unique_ptr<TEST>ret1 = retDying("dying");
  38. cout<<(*ret1)._name<<endl;
  39. unique_ptr<TEST>ret2 = retTemp("temp");
  40. cout<<(*ret2)._name<<endl;
  41. getchar();
  42. }

向后兼容:auto_ptr

  标准库较早的版本包含了一个名为auto_ptr的类,它具有unique_ptr的部分特性,但不是全部。特别时我们在容器中保存auto_ptr,也不能从函数中返回auto_ptr。虽然auto_ptr仍然是标准库的一部分,但是编写程序时应该使用unique_ptr。

向unique_ptr传递删除器

  类似于shared_ptr,unique_ptr默认情况下也是使用delete释放它指向的对象。与shared_ptr一样,我们可以重载一个unique_ptr中默认的删除器。但是unique_ptr管理删除器的方式与shared_ptr不同,其原因我们将在后面继续补充。

  重载一个unique_ptr中的删除器会影响到unique_ptr类型如何构造(或reset)该类型的对象。与重载关联器的比较操作类似。我们必须在尖括号中unique_ptr指向类型之后提供删除器类型。在创建或者reset一个这种unique_ptr这种类型的对象时,必须提供一个指定类型的可调用对象:

  1. #include <stdio.h>
  2. #include <memory>
  3. using namespace std;
  4. void closePf(FILE * pf)
  5. {
  6. cout<<"----close pf after works!----"<<endl;
  7. fclose(pf);
  8. cout<<"*****end working****"<<endl;
  9. }
  10. int main()
  11. {
  12. // FILE * fp2 = fopen("bin2.txt", "w");
  13. // if(!pf)
  14. // return -1;
  15. // char *buf = "abcdefg";
  16. // fwrite(buf, 8, 1, fp2);
  17. // fclose(fp2);
  18. //______________________________________
  19. // shared_ptr<FILE> pf(fopen("bin2.txt", "w"),closePf);
  20. // cout<<"*****start working****"<<endl;
  21. // if(!pf)
  22. // return -1;
  23. // char *buf = "abcdefg";
  24. // fwrite(buf, 8, 1, pf.get()); //!确保fwrite不会删除指针的情况下,可以将shared_ptr内置指针取出来。
  25. // cout<<"----write int file!-----"<<endl;
  26. unique_ptr<FILE,decltype(closePf)*> pf(fopen("bin2.txt", "w"),closePf); //!使用了decltype类型推断
  27. cout<<"*****start working****"<<endl;
  28. if(!pf)
  29. return -1;
  30. char *buf = "abcdefg";
  31. fwrite(buf, 8, 1, pf.get());                         //!确保fwrite不会删除指针的情况下,可以将unique_ptr内置指针取出来。
  32. cout<<"----write int file!-----"<<endl;
  33. return 0;
  34. }

使用unique_ptr管理动态数组

  标准库提供了一个可以管理new分配动态数组的unique_ptr版本。为了用用一个unique_ptr管理动态数组,我们必须在对象类型后面跟一对空方括号;如此,在unique对象销毁的时候,也可以自动调用delete[ ]而非delete来完成内存的释放。

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. class ArrTest
  5. {
  6. public:
  7. ArrTest(){
  8. static int i = 0;
  9. _i = i;
  10. cout<<" ArrTest()"<<":"<<i++<<endl;
  11. }
  12. ~ArrTest(){
  13. static int i = 0;
  14. cout<<"~ ArrTest()"<<":"<<i++<<endl;
  15. }
  16. int _i;
  17. };
  18. int main()
  19. {
  20. unique_ptr<ArrTest[]> p(new ArrTest[10]);
  21. cout<<p[4]._i<<endl; //!获取某个元素值,警告:不要使用越界的下标,unique_ptr也是不检查越界的。
  22. p.reset();
  23. return 0;
  24. }

————雁过留痕,风过留声,人的记忆是一种很不靠谱的东西。记下这些笔记,希望自己能够在需要的时候有所回忆,也希望能够帮助哪些需要获取这些知识的人。

智能指针unique_ptr的更多相关文章

  1. c/c++ 智能指针 unique_ptr 使用

    智能指针 unique_ptr 使用 和shared_ptr不同,可以有多个shared_ptr指向同一个内存,只能有1个unique_ptr指向某个内存.因此unique_ptr不支持普通的拷贝和赋 ...

  2. c++11 智能指针 unique_ptr、shared_ptr与weak_ptr

    c++11 智能指针 unique_ptr.shared_ptr与weak_ptr C++11中有unique_ptr.shared_ptr与weak_ptr等智能指针(smart pointer), ...

  3. C++智能指针 unique_ptr

    C++智能指针 unique_ptr unique_ptr 独占所指向的对象, 同一时刻只能有一个 unique_ptr 指向给定对象(通过禁止拷贝语义, 只有移动语义来实现), 定义于 memory ...

  4. 智能指针unique_ptr的用法

    unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法: std::unique_pt ...

  5. C++11 智能指针unique_ptr使用 -- 以排序二叉树为例

    用智能指针可以简化内存管理.以树为例,如果用普通指针,通常是在插入新节点时用new,在析构函数中调用delete:但有了unique_ptr类型的智能指针,就不需要在析构函数中delete了,因为当u ...

  6. C++——智能指针unique_ptr的实现

    起初,我最直观的设计想法,直接设计一个类:包含全部要素(对象,指针计数).然后提供出去. class CPoint { public: CPoint(, ) : x(xVal), y(yVal) {} ...

  7. 智能指针 unique_ptr

    unique_ptr 不共享它的指针.它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL) 算法. 1.不能进行复制构造和赋值操作(unique ...

  8. 智能指针unique_ptr记录

    unique_ptr 对对象独有管理,无法复制,共享,值传递,可以使用move语义来转移控制权. std::default_delete<int> d; std::unique_ptr&l ...

  9. 第20课 unique_ptr独占型智能指针

    一. unique_ptr的基本用法 (一)初始化方式 1. 直接初始化:unique<T> myPtr(new T);  //ok.但不能通过隐式转换来构造,如unique<T&g ...

随机推荐

  1. Lotto HDU

    链接 [http://acm.hdu.edu.cn/showproblem.php?pid=1342] 题意 分析 DFS 代码 #include<cstdio> #include< ...

  2. #个人博客作业Week2——关于代码规范的讨论

    <1> 这些规范都是官僚制度下产生的浪费大家的编程时间.影响人们开发效率, 浪费时间的东西. 反驳:官僚制度在一定程度下维持了社会的和谐稳定,一个没有法律.没有拥有完善的管理体制.完全崇尚 ...

  3. 【Beta阶段】第七次Scrum Meeting!

    每日任务内容: 本次会议为第七次Scrum Meeting会议~ 由于本次会议项目经理召开时间为10:00,在宿舍召开,召开时长约20分钟. 队员 昨日完成任务 明日要完成任务 刘乾 #177(未完成 ...

  4. js 基础-&& || 逻辑与和逻辑或

    今天百度发现一个简化长if   else if 语句的方法,看起来及其强大,感觉这样虽然对系统性能提升没有帮助但是代码更简练了,分析了一番,下面先说说自己学到的理论. 首先要弄清楚js 中对于 变量, ...

  5. 用软件工程分析开源项目octave的移植

    在本科的时候学习了软件工程,报考了信息系统项目管理师的考试,,虽然没有过,但是其实还是学了一些相关项目管理方面的知识,,9大管理,,当年应该能背出来,,, 1 项目整体管理 2 项目范围管理 3 项目 ...

  6. 第八章Jdk代理 cglib代理

    什么是代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这 ...

  7. shell脚本--continue、break

    shell中的continue和break和其他语言中的使用方法一模一样:continue用于跳过本次循环,break用于中断本层的循环 下面是使用例子: #!/bin/bash #文件名:test. ...

  8. shell脚本--文件测试

    文件测试是指测试某一个文件或者目录是否存在 测试文件格式[ 操作符 目录或者文件 ]    注意左括号和操作符之间有一个空格,文件或者目录 与右边的括号之间也有一个空格. -d 测试是否为目录 -e ...

  9. 牛客OI周赛7-普及组

    https://ac.nowcoder.com/acm/contest/372#question A.救救猫咪 #include <bits/stdc++.h> using namespa ...

  10. MYSQL ROW_FORMAT=Compact

    https://dev.mysql.com/doc/refman/5.6/en/innodb-row-format-antelope.html https://docs.oracle.com/cd/E ...