看到了迭代器这里,想到了应该把智能指针的知识总结一下了
我实现了三种智能指针,分别是auto_ptr,scoped_ptr,shared_ptr命名是根据boost库中的智能指针命名的
什么是智能指针?智能指针可以帮助你在忘记释放new出来的内存的时候自动帮助你释放内存
可以有效避免内存泄漏
例如当异常出现,跳转之后。。。内存是应该被释放的呀,一直抓住人家不放会造成内存泄漏哦
智能指针就是RAII(资源分配即初始化)一种典型的应用
利用类的构造函数和析构函数来进行内存的开辟和释放,智能指针不能完全说成是指针,它是一种类型用来管理指针的释放,当出了作用域之后,就回去进行内存释放
为了要让智能指针更像一个指针,需要对运算符进行重载:例如*引用
以上就是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 else27 {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 ap1 = new int;//支持强转(这里的意思是,new产生的int *会被强转成auto_ptr指针,就是说会拿int *构造临时的auto_ptr变量然后再赋值)56 AutoPtr ap2 = new int;57 //AutoPtr ap3(ap1);//当心,深浅拷贝58 AutoPtr 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 #include2 using namespace std; 3 4 5 6 template 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 sp1 = new int(1);53 ScopedPtr sp2 = new int(2);54 ScopedPtr sp3(sp1);55 }
执行这个代码的话就会报编译错误啦,因为拷贝构造放在私有成员里了,是不能使用哒,实现也非常简单,就把关于拷贝的东西全都丢给private就对了
接下来是共享指针shared_ptr
1 #include2 using namespace std; 3 template 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 else37 {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 s = new int(20);2 SharedPtr s1=new int (30);
因为存储的数值不同,引用计数应该是不会增长的,但是由于采用了静态变量,该类对象都使用了这个引用计数,就会都往引用计数上加,这就错了
采用指针,开辟出独有的一块空间来管理计数显然才是我们所需要的,析构的时候把delete掉的指针赋空会更好~所以有了Release函数
应该在构造函数之前加上explicit关键字,是为了防止将指针经过转换构造函数变成智能指针,就是防止
int *ptr;
autoptr<int> a;
a=ptr;
这样的转换发生