> 文档中心 > C++11 智能指针 知识整理笔记

C++11 智能指针 知识整理笔记

1、智能指针的原理

智能指针是一个类,可以在这个类的构造函数中传入一个普通指针,在析构函数中释放传入的指针。

智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。

C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持,并且第一个已经被C++11弃用。

用的比较多的是shared_ptr共享指针

2、简单了解一下四种智能指针

按照发展的时间进行展开

2.1 auto_ptr

  1. auto_ptr以前是用在C98中,C++11被抛弃,头文件一般用来作为独占指针
  2. auto_ptr被赋值或者拷贝后,失去对原指针的管理
  3. auto_ptr不能管理数组指针,因为auto_ptr的内部实现中,析构函数中删除对象使用delete而不是delete[],释放内存的时候仅释放了数组的第一个元素的空间,会造成内存泄漏。
  4. auto_ptr不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等操作。

举个栗子,会存在隐藏内存泄露问题,有拷贝语义,拷贝后源对象变得无效,这可能引发很严重的问题。

auto_ptr<int> p1(new int(5));auto_ptr<int> p2 = p1; // 编译通过,但是后面使用p1会出现内存泄漏问题

2.2 unique_ptr:

  1. C++11中用来替代auto_ptr
  2. 拷贝构造和赋值运算符被禁用,不能进行拷贝构造和赋值运算
  3. 虽然禁用了拷贝构造和赋值运算符,但unique_ptr可以作为返回值,用于从某个函数中返回动态申请内存的所有权,本质上是移动拷贝,就是使用std:move()函数,将所有权转移。
unique_ptr<int> p1(new int(5));unique_ptr<int> p2 = p1; // 编译会出错unique_ptr<int> p3 = std::move(p1); // 转移所有权, 现在那块内存归p3

2.3 share_ptr:​🔥​ ​🔥​ ​🔥​

目前使用得比较多的智能指针,要熟悉其原理

  1. 多个指针可以指向相同的对象,调用release()计数-1,计数0时资源释放
  2. use_count()查计数
  3. reset()放弃内部所有权
  4. share_ptr多次引用同一数据会导致内存多次释放
  5. 循环引用会导致死锁;
  6. 引用计数不是原子操作。

2.3.1手写智能指针类需要实现哪些函数?以及注意哪些细节

  1. 采用模板函数区设计,私有成员为指针以及指针技术;
  2. 带参的构造函数中负责引用计数的自增;
  3. 析构函数负责引用计数的减1和释放内存;
  4. 定义拷贝构造函数和赋值函数以及移动函数

2.3.2简单版的实现方法

#includeusing namespace std;template<typename T>class SharedPtr{private:T* _ptr;int* _pcount;// 指向引用计数的指针 public:SharedPtr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)){}SharedPtr(const SharedPtr& s):_ptr(s._ptr), _pcount(s._pcount){(*_pcount)++;}// 赋值构造函数SharedPtr<T>& operator=(const SharedPtr& s){if(this != &s){if(--(*(this->_pcount)) == 0){delete this->_ptr;delete this->_pcount;}_ptr = s._ptr;_pcount = s._pcount;*(_pcount)++;}return *this;} T& operator*(){return *(this->ptr);}T* operator->(){return this->ptr;}~SharedPtr(){--(*(this->_pcount ));if(*(this->_pcount == 0)){delete _ptr;_ptr = nullptr;delete _pcount;_pcount = nullptr;}}};int main(){int b = 20;int *p = &b;SharedPtr* sharep = new SharedPtr(p);}

但是shree_ptr还是无法解决一些问题

2.3.3 智能指针的循环使用

循环引用是指使用多个智能指针share_ptr时,出现了指针之间相互指向,从而形成环的情况,有点类似于死锁的情况,这种情况下,智能指针往往不能正常调用对象的析构函数,从而造成内存泄漏。

//智能指针的循环使用#include#includeusing namespace std;template<typename T>class Node{public:Node(const T& value):_pPre(NULL),_pNext(NULL),_value(value){cout<<"Node()"<<endl;}~Node(){cout<<"~Node()"<<endl;cout<<"this:"<<endl;}shared_ptr<Node<T>>_pPre;shared_ptr<Node<T>> _pNext;T _value;};void Funtest(){shared_ptr<Node<int>> sp1(new Node<int>(1));shared_ptr<Node<int>> sp2(new Node<int>(2));cout << "sp1.use_count:" << sp1.use_count() << endl;cout << "sp2.use_count:" << sp2.use_count() << endl;sp1->_pNext =sp2;//sp1的引用+1sp2->_pPre = sp1;//sp2的引用+1cout<< "sp1.use_count: "<< sp1.use_count() <<endl;cout<< "sp2.use_count: "<< sp2.use_count() <<endl; }int main(){Funtest();return 0;}

图片来源于weizhengbo
只有当sp1的计数为0时才析构,而上述情况造成了一个僵局,那就是当析构sp2时候,由于sp2->pre = sp1,sp1还在用,而sp1又会用sp2,所以sp2.use_count减减之后为1,不释放,sp1也是相同的道理,由于sp1的空间sp2还在使用中,所以sp1.use_count减减之后为1,也不释放。sp1等着sp2先释放,sp2等着sp1先释放,二者互不相让,导致最终都没能释放,内存泄漏。

在实际编程过程中,应该尽量避免出现智能指针之前相互指向的情况,如果不可避免,可以使用使用弱指针——weak_ptr,它不增加引用计数,只要出了作用域就会自动析构。

为了解决上述存在的问题,需要引入弱智能指针

4.weak_ptr

1.解决两个share_ptr互相引用产生死锁,计数永远降不到0,没办法进行资源释放,造成内存泄漏的问题。

2.使用时配合share_ptr使用,把其中一个share_ptr更换为weak_ptr。

weak_ptr用于避免shared_ptr相互指向产生的环形结构,造成的内存泄漏。weak_ptr count是弱引用个数;弱引用个数不影响shared count和对象本身,shared count为0时则直接销毁。

3 参考学习资料:

  1. 阿秀笔记
  2. C++智能指针
  3. C++智能指针-博客园
  4. 牛客网智能指针笔记

China香烟网