看到了迭代器这里,想到了应该把智能指针的知识总结一下了
我实现了三种智能指针,分别是auto_ptr,scoped_ptr,shared_ptr命名是根据boost库中的智能指针命名的
什么是智能指针?智能指针可以帮助你在忘记释放new出来的内存的时候自动帮助你释放内存
可以有效避免内存泄漏
例如当异常出现,跳转之后。。。内存是应该被释放的呀,一直抓住人家不放会造成内存泄漏哦
智能指针就是RAII(资源分配即初始化)一种典型的应用
![](https://i-blog.csdnimg.cn/blog_migrate/136afcb87eb4296b81b5eb4dd322d82f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/13d769e00c6a52fc83115e32956c167e.png)
利用类的构造函数和析构函数来进行内存的开辟和释放,智能指针不能完全说成是指针,它是一种类型用来管理指针的释放,当出了作用域之后,就回去进行内存释放
为了要让智能指针更像一个指针,需要对运算符进行重载:例如*引用
![](https://i-blog.csdnimg.cn/blog_migrate/c4f527384b4f0baf5f9802ed86fd6c89.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1875b422e7be87ff2480c39dab66da46.png)
以上就是auto_ptr的部分代码,但是有个很明显的问题,那就是没有拷贝构造函数
当没有书写拷贝构造函数使用默认的拷贝构造函数的时候,这种语句就很危险了,涉及到深浅拷贝的问题,如何解决这种问题呢?
下面就讲!~
auto_ptr ——》自动释放指针(大坑货):拷贝赋值之后,将前一个对象置空,这就完成了只释放一次(当出现拷贝的情况,两个指针分别释放其所指向的同一个空间就会boom!)
scoped_ptr——》守卫——》防拷贝:简单粗暴利用pravate不给你访问权限,不允许拷贝(既然拷贝会出错,那不拷贝不就没事了?)
shared_ptr——》共享——》引用计数:采用指针引用计数的方式进行,不采用普通类型,也不用静态变量,就是要用指针(其实最推荐使用的居然是仿拷贝指针,就是说,没必要拷贝就别拷贝了,必须拷贝再用共享指针)
贴出代码!
1 class AutoPtr 2 { 3 public: 4 explicit AutoPtr(T* ptr=NULL) 5 :_ptr(ptr) 6 { 7 } 8 AutoPtr(AutoPtr& ap) 9 :_ptr(ap._ptr) 10 { 11 ap._ptr = NULL; 12 } 13 ~AutoPtr() 14 { 15 delete _ptr; 16 } 17 AutoPtr& operator=(AutoPtr& ap) 18 { 19 if (_ptr != ap._ptr) 20 { 21 if (NULL == _ptr) 22 { 23 _ptr = ap._ptr; 24 ap._ptr = NULL; 25 } 26 else 27 { 28 delete _ptr; 29 _ptr = ap._ptr; 30 ap._ptr = NULL; 31 } 32 } 33 return *this; 34 } 35 T& operator *() 36 { 37 return *_ptr; 38 } 39 T* operator->() 40 { 41 return _ptr; 42 } 43 T* GetPtr() 44 { 45 return _ptr; 46 } 47 private: 48 T* _ptr; 49 }; 50 51 52 53 void test1() 54 { 55 AutoPtr<int > ap1 = new int;//支持强转(这里的意思是,new产生的int *会被强转成auto_ptr指针,就是说会拿int *构造临时的auto_ptr变量然后再赋值) 56 AutoPtr<int > ap2 = new int; 57 //AutoPtr<int >ap3(ap1);//当心,深浅拷贝 58 AutoPtr<int > ap4; 59 ap4 = ap1; 60 ap2 = ap1; 61 /*int *p1 = new int; 62 int *p2 = new int; 63 delete p1; 64 delete p2;*/ 65 }
PS:最长注释的那句AutoPtr<int > ap1 = new int;如果不希望这种事情发生的话要用到explicit关键字
我把测试用例也贴出来了~auto_ptr的实现还是非常简单的,但是就是太坑了,最好不要使用~
接下来是防拷贝智能指针scoped_ptr
1 #include<iostream> 2 using namespace std; 3 4 5 6 template<class T> 7 class ScopedPtr 8 { 9 public: 10 explicit ScopedPtr(T* ptr=NULL) 11 :_ptr(ptr) 12 { 13 } 14 ~ScopedPtr() 15 { 16 if (_ptr) 17 { 18 delete _ptr; 19 } 20 } 21 T* operator ->() 22 { 23 return _ptr; 24 } 25 T& operator *() 26 { 27 return *_ptr; 28 } 29 T* GetPtr() 30 { 31 return _ptr; 32 } 33 34 private: 35 ScopedPtr(ScopedPtr& sp) 36 { 37 38 } 39 ScopedPtr& operator=() 40 { 41 42 } 43 private: 44 T* _ptr; 45 }; 46 47 48 49 void test1() 50 { 51 52 ScopedPtr<int> sp1 = new int(1); 53 ScopedPtr<int> sp2 = new int(2); 54 ScopedPtr<int> sp3(sp1); 55 }
执行这个代码的话就会报编译错误啦,因为拷贝构造放在私有成员里了,是不能使用哒,实现也非常简单,就把关于拷贝的东西全都丢给private就对了
接下来是共享指针shared_ptr
1 #include<iostream> 2 using namespace std; 3 template<class T> 4 class SharedPtr 5 { 6 public: 7 explicit SharedPtr(T* ptr) 8 :_ptr(ptr) 9 ,_pCount(new int(1)) 10 { 11 12 } 13 SharedPtr(SharedPtr& sp) 14 :_ptr(sp._ptr) 15 ,_pCount(sp._pCount) 16 { 17 (*_pCount)++; 18 } 19 ~SharedPtr() 20 { 21 if (--(*_pCount) == 0) 22 { 23 delete _ptr; 24 delete _pCount; 25 } 26 } 27 SharedPtr& operator=(SharedPtr& sp) 28 { 29 if (_ptr != sp._ptr) 30 { 31 if (NULL == _ptr) 32 { 33 _ptr = sp._ptr; 34 _pCount = sp._pCount; 35 } 36 else 37 { 38 _Release(); 39 } 40 } 41 return *this; 42 } 43 T& operator*() 44 { 45 return *_ptr; 46 } 47 T* operator->() 48 { 49 return _ptr; 50 } 51 protected: 52 void _AddRef() 53 { 54 ++(*_pCount); 55 } 56 void _Release() 57 { 58 if (--(*_pCount) == 0) 59 { 60 delete _ptr; 61 delete _pCount; 62 _ptr = NULL; 63 _pCount = NULL; 64 } 65 } 66 private: 67 T* _ptr; 68 int* _pCount; 69 };
引用计数是用指针来实现的,一开始采用的是静态变量,但是有个问题,当声明
1 SharedPtr<int> s = new int(20); 2 SharedPtr<int> s1=new int (30);
因为存储的数值不同,引用计数应该是不会增长的,但是由于采用了静态变量,该类对象都使用了这个引用计数,就会都往引用计数上加,这就错了
采用指针,开辟出独有的一块空间来管理计数显然才是我们所需要的,析构的时候把delete掉的指针赋空会更好~所以有了Release函数
应该在构造函数之前加上explicit关键字,是为了防止将指针经过转换构造函数变成智能指针,就是防止
int *ptr;
autoptr<int> a;
a=ptr;
这样的转换发生