智能指针:
为什么需要智能指针?
1. malloc出来的空间,没有进行释放,存在内存泄漏的问题。
2. 异常安全问题。如果在malloc和free之间如果存在抛异常,那么还是有内存泄漏。这种问题就叫异常安 全。
RAII:
是一种利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥俩个等等)的简单技术
优点:
1.不需要显示地释放资源
2.采用这种方式,对象所需地资源在其生命周期内始终保持有效
// 使用RAII思想设计的SmartPtr类 template<class T> class SmartPtr { public: SmartPtr(T* ptr = nullptr) : _ptr(ptr) {} ~SmartPtr() { if(_ptr) delete _ptr; } private: T* _ptr; }; void MergeSort(int* a, int n) { int* tmp = (int*)malloc(sizeof(int)*n); // 讲tmp指针委托给了sp对象,用时老师的话说给tmp指针找了一个可怕的女朋友!天天管着你,直到你go die^^ SmartPtr<int> sp(tmp); // _MergeSort(a, 0, n - 1, tmp); // 这里假设处理了一些其他逻辑 vector<int> v(1000000000, 10); // ... } int main() { try { int a[5] = { 4, 5, 2, 3, 1 }; MergeSort(a, 5); } catch(const exception& e) { cout<<e.what()<<endl; } return 0; }
智能指针原理:
需要具备指针地行为,可以解引用,也可以通过->去访问所指空间中地内容,因此还需要将*, ->,重载
template<class T> class SmartPtr { public: SmartPtr(T* ptr = nullptr) : _ptr(ptr) {} ~SmartPtr() { if(_ptr) delete _ptr; } T& operator*() {return *_ptr;} T* operator->() {return _ptr;} private T* _ptr; }; struct Date { int _year; int _month; int _day; }; int main() { SmartPtr<int> sp1(new int); *sp1 = 10 cout<<*sp1<<endl; SmartPtr<int> sparray(new Date); // 需要注意的是这里应该是sparray.operator->()->_year = 2018; // 本来应该是sparray->->_year这里语法上为了可读性,省略了一个-> sparray->_year = 2018; sparray->_month = 1; sparray->_day = 1; }
总结智能指针的原理:
1.RAII特性
2.重载opreator* 和operator->,具有像指针一样的行为
C++11和boost中智能指针的关系
-
C++98中产生了第一个智能指针auto_ptr
-
C++boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr
-
C++11,引入了unique_ptr和share_ptr和weak_ptr.需要注意的是unique_ptr对应boost的scoped_ptr.并且这些智能指针是实现原理是参考boost中的实现的
auto_ptr { 构造->保存指针信息 析构->释放指针空间 auto_ptr<int> ptr(ptr2); 拷贝构造->(转移管理权的形式,将ptr2赋值给_ptr,置空ptr2) auto_ptr<int> ptr = ptr2; 赋值运算符重载->(转移管理权,判断是否自己给自己赋值,先释放自己所指向的空间,在用ptr2赋值给_ptr,置空ptr2) }
unique_ptr { //线程安全 //不支持拷贝构造 C++11: unique_ptr(const unique+ptr<T>& ap) = delete; unique_ptr<T>& opreator=(const unique_ptr<T>& ap) = delete; C++98: //只声明,不实现,声明成私有 }
shared_ptr { //会存在引用计数的线程安全问题 //实现了引用计数的方式 //////////////////////////////////////////////////////////////////////////////////// //此方法存在一定问题 private: T* _ptr statice int _count; //使用statice会存在一定问题: //使用了statice后每个对象只会公用一块空间,而当需要多块空间来计数的时候就不能实现 //sp1; sp2(sp1)-> 1和2公用一个空间 sp3; sp4(sp3);->3和4公用一块空间 //这样就需要两个引用计数,但是statice后一个类只能存在一个引用计数 //////////////////////////////////////////////////////////////////////////////////// public: shared_ptr(T* ptr) :_ptr(ptr) ,_pcount(new int(1)) {} shared_ptr(const shared_ptr<T>& sp) :_ptr(sp._ptr) ,_pcount(sp._pcount) { (*_pcount)++; } shared_ptr<T>& operator=(const shared_ptr<T>& sp) { if(this != &sp) { //ptr1; ptr2(ptr1) //ptr3; ptr3 = ptr2; //先判断ptr2的计数是否为0,不为0则不能释放,说明其他指针还在使用 if(--(*_pcount) == 0) { delete _pcount; delete _ptr; } _ptr = sp._ptr; _pcount = sp._pcount; (*_pcount)++; } return *this; } ~shared_ptr() { if(--(*_pcount) == 0) { delete _ptr; delete _pcount; _pcount = nullptr; _ptr = nullptr; } } private: T* _ptr; int* _pcount; //_pcount 在堆上,会存在线程安全问题 }
//shared_ptr 修改确保shared_ptr的线程安全问题 shared_ptr { public: shared_ptr(T* ptr) :_ptr(ptr) ,_pcount(new int(1)) {} shared_ptr(const shared_ptr<T>& sp) :_ptr(sp._ptr) ,_pcount(sp._pcount) { (*_pcount)++; } shared_ptr<T>& operator=(const shared_ptr<T>& sp) { if(this != &sp) { //ptr1; ptr2(ptr1) //ptr3; ptr3 = ptr2; //先判断ptr2的计数是否为0,不为0则不能释放,说明其他指针还是只用 if(--(*_pcount) == 0) { delete _pcount; delete _ptr; } _ptr = sp._ptr; _pcount = sp._pcount; (*_pcount)++; } return *this; } ~shared_ptr() { if(--(*_pcount) == 0) { delete _ptr; delete _pcount; _pcount = nullptr; _ptr = nullptr; } } private: T* _ptr; int* _pcount; //_pcount 在堆上,会存在线程安全问题 } struct Date{ int _year; int _month; int _day; };
shared_ptr 的循环引用问题:
在链表的情况:
struct ListNode{ //ListNode* _next; //ListNode* _prev; /* std::shared_ptr<ListNode> _next; std::shared_ptr<ListNode> _prev; */ std::weak_ptr<ListNode> _next; std::weak_ptr<ListNode> _prev; ~ListNode() { cout << "~ListNode()" << endl; } }; int main() { std::shared_ptr<ListNode> node1(new ListNode); std::shared_ptr<ListNode> node2(new ListNode); cout << node1.use_count() << endl; cout << node2.use_count() << endl; node1->_next = node2; node2->_prev = node1; cout << node1.use_count() << endl; cout << node2.use_count() << endl; return 0; }
week_ptr——>为了解决shared_ptr产生的循环引用,它并不是RAII思想
特点:
1.不会增加引用计数
2.只可以接收shared_ptr赋值给它
3.本质并不是一个智能指针