• 智能指针实质是一个对象,行为表现的像指针
  • RAII(资源获取即初始化)对普通指针进行封装
  • 值语义转换成引用语义
  • 避免出错跳转catch导致try中的delete没有运行

智能指针的使用

shared_ptr使用

shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁

  • 初始化:构造函数或make_shared
  • 拷贝和赋值
    • 拷贝使引用+1;
    • 赋值使得原对象引用-1;
  • get获得原始指针
  • 注意不要用一个原始指针初始化多个shared_ptr,会造成double free
  • 注意避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。

    循环引用

    ```cpp class B; // 前置声明 class A { public: shared_ptr ptr; };

class B { public: shared_ptr ptr; };

int main() { while(true) { shared_ptr pa(new A()); shared_ptr pb(new B()); pa -> ptr = pb; pb -> ptr = pa; } return 0; }

  1. <a name="sDSbQ"></a>
  2. ## ![](https://cdn.nlark.com/yuque/0/2021/jpeg/2728415/1615903479013-4cecba9c-1ff3-402e-b326-7fd5ac958ce5.jpeg#align=left&display=inline&height=371&margin=%5Bobject%20Object%5D&originHeight=371&originWidth=562&size=0&status=done&style=none&width=562)
  3. 上图中,class A和class B的对象各自被两个智能指针管理,也就是A object和B object引用计数都为2,为什么是2?
  4. 分析class A对象的引用情况,该对象被main函数中的pa和class B对象中的ptr管理,因此A object引用计数是2,B object同理。
  5. 在这种情况下,在main函数中一个while循环结束的时候,pa和pb的析构函数被调用,但是class A对象和class B对象仍然被一个智能指针管理,A object和B object引用计数变成1,于是这两个对象的内存无法被释放,造成内存泄漏,如下图所示<br />![](https://cdn.nlark.com/yuque/0/2021/jpeg/2728415/1615903505786-b6bd3579-07b5-49dc-bbb6-c93e548b75fd.jpeg#align=left&display=inline&height=401&margin=%5Bobject%20Object%5D&originHeight=401&originWidth=557&size=0&status=done&style=none&width=557)
  6. <a name="XeWoV"></a>
  7. ## unique_ptr使用
  8. 同一时刻只能有一个unique_ptr指向给定对象(通过禁止**拷贝语义,**只有**移动语义**来实现)。
  9. - 生命周期:unique_ptr指针创建时开始,直到离开作用域
  10. - 对象销毁(默认使用delete操作符,用户可指定其他操作)
  11. - 智能指针所指对象改变:构造函数,reset方法重新指定,release,移动语义
  12. <a name="fAbWk"></a>
  13. ## weak_ptr(资源观测权)的使用
  14. 为配合shared_ptr的使用,不具备普通指针的行为,没有重载`operator*和->`,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
  15. - 可从shared_ptr或者weak_ptr对象构造,获得资源观测权
  16. - 构造不会引起指针引用计数增加
  17. - use_count():引用计数;expired(),不复存在
  18. - lock():从被观测的shared_ptr中获取shared_ptr对象,但当expired()==true时,lock()函数将返回一个存储空指针的shared_ptr
  19. <a name="S5NGe"></a>
  20. ## 实现原理
  21. > 智能指针将一个**计数器与类指向的对象**相关联,引用计数跟踪该类有多少个对象共享同一指针
  22. - 每次创建类的对象时,初始化指针并将引用计数置1;
  23. - 拷贝构造函数:拷贝指针并增加对应的引用计数。
  24. - 赋值拷贝:减少左操作数的引用计数并增加右操作数
  25. - 析构函数: 减少引用计数
  26. - 重载`->`和`*`
  27. - 自动销毁:栈对象的有限作用域以及临时对象(**有限作用域**实现)析构函数释放内存
  28. ```cpp
  29. template<typename T>
  30. class SmartPointer{
  31. private:
  32. T* _ptr;
  33. size_t* _count;
  34. public:
  35. SmartPointer(T* ptr==nullptr):
  36. _ptr(ptr){
  37. if(_ptr){
  38. _count=new size_t(1);
  39. }else{
  40. _count=new size_t(0);
  41. }
  42. }
  43. SmartPointer(const SmartPointer& _sp){
  44. if(this!=&_sp){
  45. this->_ptr=_sp._ptr;
  46. *this->_count=_sp._count;
  47. (*this->_count)++;
  48. }
  49. }
  50. SmartPointer& operator=(const SmartPointer& _sp){
  51. if(this!=&sp){
  52. if(this->_ptr){
  53. (*this->_count)--;
  54. if((*this->_count)==0){
  55. delete this->_count;
  56. delete this->_ptr;
  57. }
  58. }
  59. this->_ptr=_sp._ptr;
  60. this->_count=_sp._count;
  61. (*this->_count)++;
  62. return *this;
  63. }
  64. }
  65. T& operator*() const{
  66. assert(this->_ptr!=nullptr);
  67. return *(this->_ptr);
  68. }
  69. T* operator->() const {
  70. assert(this->_ptr!=nullptr);
  71. return this->_ptr;
  72. }
  73. T* get() const {
  74. return this->_ptr;
  75. }
  76. ~SmartPointer(){
  77. (*this->_count)--;
  78. if((*this->_count)==0){
  79. delete this->_count;
  80. delete this->_ptr;
  81. }
  82. }
  83. size_t use_count() const{
  84. return *(this->_count);
  85. }
  86. bool expired() const {
  87. return use_count()==0;
  88. }
  89. }