原创文章 转载请注明出处http://blog.csdn.net/effective_coder
欢迎来到内存这块雷区,在对内存分配器进一步讨论之前我觉得有必要先讨论一下STL中的auto_ptr,我也加了很多STL的讨论社区以及很多群,长久以来发现了一个很大的问题,太多的人反映出不敢用auto_ptr,原因是好多好多未定义行为,用到最后自己都摸不着头脑了,其实如果来分析一下他的来龙去脉,也不是那么难!为此我采取分析STL的auto_ptr和boost里面的shared_ptr两种不同风格的智能指针,希望更具全面性!(注:在0X标准中auto_ptr已经取消了,被更高级的指针替代,不过这里还是讨论一下)
在我另一篇文章中对auto_ptr有一些基本的介绍auto_ptr简介 对于怎么用智能指针 不属于本文范畴 看过那篇文章对于auto_ptr也应该了解了!
这里多说一些,在我们的程序中经常出现
ClassA *ptr=new ClassA;
```````````````````中间步骤`````````
Delete ptr;
这样的风格,中间步骤发生异常或者是忘记加delete,或者是delete之后忘记置0 ,造成内存泄露和使用错误,针对这个问题如何解决,在C++ primer上面为我们提供了了两种解决方案,
1:设置拥有权的转移
2:使用引用计数的方式
我记得大一的时候看到这两点,完全看不懂是什么意思,呵呵,现在终于可以一笑置之了!
所以针对这个两个解决方案,出现了两种风格的智能指针,STL中的auto_ptr属于拥有权转移指针,boost中的shared_ptr属于引用计数型(boost里面的智能指针有6个,这里只是其中一个)
针对auto_ptr:
对于设置拥有权限转移的指针,我们需要做的就是在复制和赋值的时候重定向指针,使得他们不会出现多个指针指向同一个对象,出现指针悬空的现象,在C++ primer 591页有一个针对auto_ptr的表格,列出了所有成员函数以及相应的说明,其实我觉得就算不懂STL源码,在C++ primer看到591页的情况下,写出这个模拟的auto_ptr不是什么难事,先看我的源码 :
template<typename Ty> class Auto_ptr { private: Ty *ap; public: explicit Auto_ptr (Ty * ptr = 0)throw() :ap(ptr) {//构造函数,指定explicit不能隐式使用 } Auto_ptr (Auto_ptr &r)throw() :ap( r.release() ) { //复制构造,注意看参数 } template<typename _other> Auto_ptr (Auto_ptr<_other> &other )throw() :ap (other.release() ) {//重载的模版复制函数,作用稍后细说 } Auto_ptr& operator= (Auto_ptr &other)throw() {//赋值操作符,同样转移了拥有权 reset( other.release() ); return *this; } template<typename Y> Auto_ptr& operator= (Auto_ptr<Y> &other)throw() {//重载,同复制构造函数 reset( other.release() ); return *this; } //重置函数,带默认参数 void reset( Ty* ptr = 0 )throw(){ if( ptr != ap ) { delete ap; ap = ptr; } } //该函数释放掉自己的对象,并返回 Ty* release ()throw(){ Ty* temp( ap ); ap = 0; return temp; } //这个接口标准里面没有,是我自己加的,我觉得有必要转型 operator bool()throw(){ return ap; } ~Auto_ptr()throw(){ //析构函数 delete ap; } Ty* get() const throw(){ return ap; } Ty& operator* ()throw(){ return *ap; } Ty* operator-> ()throw(){ return ap; } };
代码不多,也很好懂,这里我特别说明一下几点:
1:构造函数一定要加explicit关键字,因为auto_ptr只能显式的构造(参见auto_ptr标准)
2:复制构造函数的参数千万不能加const,因为加了const就成了锁定指针了,拥有权再也转移不出去了,何谈复制(同赋值操作符)
3:为什么要重载一个模版函数,难道指针之间可以互相复制或赋值吗?其实我们这里这样写主要是为了基类和派生类的转换,这样可以不影响多态性的发挥!
4:接下来的几个函数见名知意,很简单,而且我们的Auto_ptr没有++ --操作符,这个根据需要自己定制,当标准的智能指针不满足我们需要可以自己随便改啊(只要你喜欢)
5:这点很重要,现在这个类还不完整,没有const的功能,即锁定拥有权不转移的功能,为此参考了Greg Colvin的一份实例,他的做法有点高级过头了
template <typename T> struct ref_auto_ptr { T* p; ref_auto_ptr(T *r = 0)throw() :p(r){ //仅只有成员和构造 } }
首先在我们的类之前添加这么一个类 然后在Auto_ptr中添加以下成员函数:
Auto_ptr(ref_auto_ptr<Ty> &rh)throw() :ap(rh.p) { } Auto_ptr& operator =(ref_auto_ptr<Ty> &rh)throw() { reset(rh.p); return *this; } template<typename T> operator ref_auto_ptr<T> ()throw() { return ref_auto_ptr( release() ); } template<typename T> operator Auto_ptr<T> ()throw() { return Auto_ptr<T>( release() ); }
说实话我还不是很明白这个转型代码,就算明白点也感觉说不出来,他把右值转化为左值,这样子非const的可以转化,但是const就不能转化,这个怎么解释清楚点,希望留言指点!
到现在我们基本的Auto_ptr功能完备实现了,测试一下:
#include "auto_ptr.h" int _tmain(int argc, _TCHAR* argv[]) { Auto_ptr<int> ptr( new int(1024) ); Auto_ptr<int> ptr2; ptr2 = ptr; cout<< *ptr2 <<" "<<endl; //正常转移 输出结果1024 const Auto_ptr<int> ptr3( new int(1024) ); Auto_ptr<int> ptr4; ptr4 = ptr3; //编译期错误“没有找到接受“const Auto_ptr<Ty>”类型的右操作数的运算符(或没有可接受的转换)” system("pause"); return 0; }
下一节 :定制智能指针 shared_ptr
求助:希望懂的人给我解释下,上面那个const转型是如何把右值转成了左值,谢谢!