1、智能指针的原理智能指针是一个类,可以在这个类的构造函数中传入一个普通指针,在析构函数中释放传入的指针。
智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。
C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持,并且第一个已经被C++11弃用。
用的比较多的是shared_ptr共享指针
2、简单了解一下四种智能指针按照发展的时间进行展开
2.1 auto_ptr- auto_ptr以前是用在C98中,C++11被抛弃,头文件一般用来作为独占指针auto_ptr被赋值或者拷贝后,失去对原指针的管理 。auto_ptr不能管理数组指针,因为auto_ptr的内部实现中,析构函数中删除对象使用delete而不是delete[],释放内存的时候仅释放了数组的第一个元素的空间,会造成内存泄漏。auto_ptr不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等操作。
举个栗子,会存在隐藏内存泄露问题,有拷贝语义,拷贝后源对象变得无效,这可能引发很严重的问题。
auto_ptr2.2 unique_ptr:p1(new int(5)); auto_ptr p2 = p1; // 编译通过,但是后面使用p1会出现内存泄漏问题
- C++11中用来替代auto_ptr拷贝构造和赋值运算符被禁用,不能进行拷贝构造和赋值运算虽然禁用了拷贝构造和赋值运算符,但unique_ptr可以作为返回值,用于从某个函数中返回动态申请内存的所有权,本质上是移动拷贝,就是使用std:move()函数,将所有权转移。
unique_ptr2.3 share_ptr: p1(new int(5)); unique_ptr p2 = p1; // 编译会出错 unique_ptr p3 = std::move(p1); // 转移所有权, 现在那块内存归p3
目前使用得比较多的智能指针,要熟悉其原理
- 多个指针可以指向相同的对象,调用release()计数-1,计数0时资源释放use_count()查计数;reset()放弃内部所有权;share_ptr多次引用同一数据会导致内存多次释放 ;循环引用会导致死锁;引用计数不是原子操作。
- 采用模板函数区设计,私有成员为指针以及指针技术;带参的构造函数中负责引用计数的自增;析构函数负责引用计数的减1和释放内存;定义拷贝构造函数和赋值函数以及移动函数
#includeusing namespace std; template 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 & 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#include using namespace std; template class Node { public: Node(const T& value):_pPre(NULL),_pNext(NULL),_value(value){ cout<<"Node()"< >_pPre; shared_ptr > _pNext; T _value; }; void Funtest() { shared_ptr > sp1(new Node (1)); shared_ptr > sp2(new Node (2)); cout << "sp1.use_count:" << sp1.use_count() << endl; cout << "sp2.use_count:" << sp2.use_count() << endl; sp1->_pNext =sp2;//sp1的引用+1 sp2->_pPre = sp1;//sp2的引用+1 cout<< "sp1.use_count: "<< sp1.use_count() <
只有当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_ptr1.解决两个share_ptr互相引用产生死锁,计数永远降不到0,没办法进行资源释放,造成内存泄漏的问题。
2.使用时配合share_ptr使用,把其中一个share_ptr更换为weak_ptr。
3 参考学习资料:weak_ptr用于避免shared_ptr相互指向产生的环形结构,造成的内存泄漏。weak_ptr count是弱引用个数;弱引用个数不影响shared count和对象本身,shared count为0时则直接销毁。
阿秀笔记C++智能指针C++智能指针-博客园牛客网智能指针笔记



