文章目录
为了解决指针资源忘记或没有删除导致的内存泄露问题,C++就出现了智能指针的机制,可以在使用的时候初始化,在离开作用域之后就自动析构,删除资源
C++98的智能指针 auto_ptr的模拟实现 auto_ptr是最早期的智能指针形态, 它可以实现在
构造函数里面初始化
在析构函数里面将资源销毁,不用我们去显示调用,避免内存泄露
但是它无法解决拷贝构造的问题,它是浅拷贝(会造成资源被多次析构)(使用管理权转移的方式来进行拷贝构造即构造之后,自己的资源就没了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 namespace xzw { template <class T > class auto_ptr { private : T *_ptr; public : auto_ptr (T *ptr) : _ptr(ptr) { } ~auto_ptr () { if (_ptr) { cout << "delete" << endl; delete _ptr; } } auto_ptr (auto_ptr<T> &sp) : _ptr(sp._ptr) { sp._ptr = nullptr ; } T &operator *() { return *_ptr; } T *operator ->() { return _ptr; } }; };void demo1 () { int *p1 = new int ; xzw::auto_ptr<int > sp1 (p1) ; int *p2 = new int ; xzw::auto_ptr<int > sp2 (sp1) ; xzw::auto_ptr<int > sp3 (new int ) ; *sp3 = 10 ; cout << __LINE__ << *sp3 << endl; cout << *sp1 << endl; }
实际工作中绝对不能使用auto_ptr
C++11的智能指针 定制删除器 默认情况下,智能指针底层的删除器都是用delete 但是不同的资源销毁的方式不同,直接用delete十分暴力,不合理,所以就有了定制删除器
比如:
malloc -> free
open -> close
fopen -> fclose
new[] -> delete[] …
我们在下文会详细解释
unique_ptr的模拟实现 unique_ptr对于拷贝构造的解决方式即直接把拷贝构造给禁止了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 namespace Uni_Ptr { template <class T > class defult_delete { public : void operator () (const T* ptr) { cout<<__LINE__<<endl; cout<<"delete" <<endl; delete ptr; } }; template <class T , class D =default_delete<T>> class unique_ptr { private : T *_ptr; unique_ptr (const unique_ptr<T> &sp) = delete ; public : unique_ptr (T *ptr) : _ptr(ptr) { } ~unique_ptr () { if (_ptr) { D del; del (_ptr); } } T &operator *() { return *_ptr; } T *operator ->() { return _ptr; } }; };
1 2 3 4 5 6 void demo2 () { Uni_Ptr::unique_ptr<int > sp1 (new int ) ; }
unique_ptr对于定制删除器的使用,就是我们在外面写一个类的仿函数,在模板里面进行传参即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 template <class T >struct DeleteArray { void operator () (const T *ptr) { cout << "delete[]" << endl; delete [] ptr; } };#include <cstdio> #include <stdio.h> struct DeleteFile { void operator () (FILE *ptr) { cout << "fclose:" << endl; fclose (ptr); } };void demo6 () { Uni_Ptr::unique_ptr<Date> s (new Date) ; Uni_Ptr::unique_ptr<Date, DeleteArray<Date>> s1 (new Date[10 ]); Uni_Ptr::unique_ptr<FILE,DeleteFile> s2 (fopen("1.txt" ,"w" )) ; }
shared_ptr的模拟实现 shared_ptr是为了解决unique_ptr无法实现拷贝构造 新增加了一个引用计数的机制:
同一个对象只能有一个引用计数,当调用构造函数的时候,第一个引用计数出现为1,后续如果有发生拷贝构造,引用计数就+1,当析构的时候,引用计数就-1,直到引用计数为0的时候,这个资源就销毁
即由最后一个管理的对象来进行对资源的释放
智能指针是线程安全的,因为对里面的引用计数进行了加锁处理,但是指向的资源不是线程安全的,这个需要使用者手动处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 namespace Shared_Ptr { template <class T > class defult_delete { public : void operator () (const T* ptr) { cout<<__LINE__<<endl; delete ptr; } }; template <class T , class D =default_delete<T>> class shared_ptr { private : T *_ptr; int *_pRefCount; mutex *_mtx; public : shared_ptr (T *ptr) : _ptr(ptr), _pRefCount(new int (1 )), _mtx(new mutex) { } shared_ptr (const shared_ptr<T> &sp) : _ptr(sp._ptr), _pRefCount(sp._pRefCount), _mtx(sp._mtx) { AddRef (); } ~shared_ptr () { Release (); } T *get () const { return _ptr; } T &operator *() { return *_ptr; } T *operator ->() { return _ptr; } void Release () { _mtx->lock (); (*_pRefCount)--; bool flag = false ; if (!(*_pRefCount) && _ptr) { cout << "delete" << endl; delete _ptr; delete _pRefCount; _ptr = nullptr ; _pRefCount = nullptr ; flag = true ; } _mtx->unlock (); if (flag) { delete _mtx; _mtx = nullptr ; } } void AddRef () { _mtx->lock (); (*_pRefCount)++; _mtx->unlock (); } shared_ptr<T> &operator =(const shared_ptr<T> &sp) { if (_ptr != sp._ptr) { Release (); _ptr = sp._ptr; _pRefCount = sp._pRefCount; _mtx = sp._mtx; AddRef (); } return *this ; } }; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void demo3 () { Shared_Ptr::shared_ptr<int > sp (new int ) ; Shared_Ptr::shared_ptr<int > sp1 (sp) ; Shared_Ptr::shared_ptr<int > sp2 (sp) ; Shared_Ptr::shared_ptr<int > sp3 (sp) ; Shared_Ptr::shared_ptr<int > sp4 (new int (3 )) ; sp1 = sp4; sp2 = sp4; sp = sp4; *sp4 = 3 ; *sp = 4 ; cout << (*sp3) << endl; cout << __LINE__ << endl; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 struct Date { int _year = 1 ; int _month = 1 ; int _day = 1 ; };void SharePtreFunc (Shared_Ptr::shared_ptr<Date> &sp, size_t n, mutex &mtx) { for (int i = 0 ; i < n; i++) { Shared_Ptr::shared_ptr<Date> copy (sp) ; { unique_lock<mutex> lock (mtx) ; copy->_day++; copy->_month++; copy->_year++; lock.unlock (); } cout << "hello" << endl; } }void demo4 () { Shared_Ptr::shared_ptr<Date> p (new Date) ; const size_t n = 10000000 ; mutex mtx; thread t1 (SharePtreFunc, std::ref(p), n, std::ref(mtx)) ; thread t2 (SharePtreFunc, std::ref(p), n, std::ref(mtx)) ; t1.join (); t2.join (); cout << p->_day << endl; cout << p->_month << endl; cout << p->_year << endl; }
shared_ptr对于定制删除器的使用:就是在构造函数的时候加上,该可调用对象(函数指针,仿函数,lambda表达式) 使用lambda表达式最方便
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 template <class T >struct DeleteArray { void operator () (const T *ptr) { cout << "delete[]" << endl; delete [] ptr; } };#include <cstdio> #include <stdio.h> struct DeleteFile { void operator () (FILE *ptr) { cout << "fclose:" << endl; fclose (ptr); } };void demo6 () { std::shared_ptr<Date> srp (new Date) ; std::shared_ptr<Date> srp4 (new Date[10 ],DeleteArray<Date>()) ; std::shared_ptr<Date> srp3 (new Date[10 ],[](Date* ptr){cout<<"delete []" <<endl; delete [] ptr;}) ; std::shared_ptr<FILE> srp1 (fopen("1.txt" ,"w" ),DeleteFile()) ; std::shared_ptr<FILE> srp2 (fopen("1.txt" ,"w" ),[](FILE* ptr){fclose(ptr);}) ; }
weak_ptr shared_ptr会出现循环引用的问题 一个指针,你里面有一个智能指针,指向我,我里面有一个智能指针指向你,这就是循环引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct ListNode { int _val; shared_ptr<ListNode> _prev; shared_ptr<ListNode> _next; };void demo5 () { shared_ptr<ListNode> n1 (new ListNode) ; shared_ptr<ListNode> n2 (new ListNode) ; cout << n1.use_count () << endl; cout << n2.use_count () << endl; n1->_prev=n2; n2->_next=n1; cout << n1.use_count () << endl; cout << n2.use_count () << endl; }
这样子会出现问题
weak_ptr是一个弱指针,没有引用计数的机制,可以支持shared_ptr对它进行拷贝构造和赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 template <class T > class weak_ptr { private : T *_ptr; public : weak_ptr () : _ptr(nullptr ) { } weak_ptr (const shared_ptr<T> &sp) : _ptr(sp.get ()) { } weak_ptr<T> &operator =(const shared_ptr<T> &sp) { _ptr = sp.get (); return *this ; } }; struct ListNode { int _val; Shared_Ptr::weak_ptr<ListNode> _prev; Shared_Ptr::weak_ptr<ListNode> _next; };void demo5 () { shared_ptr<ListNode> n1 (new ListNode) ; shared_ptr<ListNode> n2 (new ListNode) ; cout << n1.use_count () << endl; cout << n2.use_count () << endl; n1->_prev=n2; n2->_next=n1; cout << n1.use_count () << endl; cout << n2.use_count () << endl; }
总结一点:如果使用对象的话,就要用强指针,如果要引用一个对象的话,就要用弱指针 即weak_ptr不会参与空间资源的管理,只是作为一个解决循环引用的工具