智能指针(auto_ptr)vc版
auto_ptr包含于头文件 #include<memory> 其中<vector><string>这些库中也存有。auto_ptr 能够方便的管理单个堆内存对象,在你不用的时候自动帮你释放内存。
auto_ptr的设计目的:
局部对象获取的资源(内存),当函数退出时,它们的析构函数被调用,从而自动释放这些资源,但是,如果以显式手法获得的资源(称为动态分配内存空间如:new、malloc等)没有绑定在任何对象身上,必须以显式手法释放。(如:delete,free等)。
因此,这会出现一些麻烦,如我们忘掉delete,或者return语句在delete之前,例如:
1 #include<iostream> 2 using namespace std; 3 int main() 4 { 5 int *p=new int(10); 6 if(*p>2) 7 return 0; 8 delete p; 9 p=NULL; 10 }
在没有delete之前函数已经返回。同时,如果此函数有异常出现时,函数将立刻退离,根本不会调用函数尾端的delete语句,这就会使内存遗失。当然我们也可以防止这种情况发生那就是在函数中用try。。catch语句,但是这回显的程序很复杂和累赘。
如果使用智能指针,无论在任何情况下,只要自身摧毁就一定会释放资源。因为智能指针原理就是将指针转换为类对象,由于对象被销毁会自动调用类中的析构函数,释放对象所指向的内存空间从而达到自动释放资源的目的。
注意:auto_ptr它是“它所指向的对象”的拥有者。所以。当身为对象拥有者的auto_ptr被摧毁,auto_ptr要求一个对象只有一个拥有者,严禁一物二主(之后的拥有权转移也会提到)
一个版本的auto_ptr是Linux和VS中使用的,另一个是VC版本的,下面是VC版本的auto_ptr的源代码剖析:
c中的auto_ptr:
源码:
vc: template<class _Ty> class auto_ptr { public: typedef _Ty element_type; explicit auto_ptr(_Ty *_P = 0) _THROW0() : _Owns(_P != 0), _Ptr(_P) {} auto_ptr(const auto_ptr<_Ty>& _Y) _THROW0() : _Owns(_Y._Owns), _Ptr(_Y.release()) {} auto_ptr<_Ty>& operator=(const auto_ptr<_Ty>& _Y) _THROW0() {if (this != &_Y) {if (_Ptr != _Y.get()) {if (_Owns) delete _Ptr; _Owns = _Y._Owns; } else if (_Y._Owns) _Owns = true; _Ptr = _Y.release(); } return (*this); } ~auto_ptr() {if (_Owns) delete _Ptr; } _Ty& operator*() const _THROW0() {return (*get()); } _Ty *operator->() const _THROW0() {return (get()); } _Ty *get() const _THROW0() {return (_Ptr); } _Ty *release() const _THROW0() {((auto_ptr<_Ty> *)this)->_Owns = false; return (_Ptr); } private: bool _Owns; _Ty *_Ptr; };
解析代码:
1.
#include <iostream> #include <vld.h> int main() { int *p=new int(10)//初始化指针 return 0; }
运行结果:
由于没有用delete释放内存空间,所以造成内存空间浪费了四个字节。
2.
#include<iostream> using namespace std; class Test { public: void fun() { cout << "Test::fun()" << endl; } }; template<class _Ty> class auto_ptr { public: explicit auto_ptr(_Ty *_P = 0) :_Owns(_P != 0), _Ptr(_P) {} auto_ptr(const auto_ptr<_Ty>&_Y):_Owns(_Y._Owns),_Ptr(_Y.release()){} auto_ptr<_Ty>&operator=(const auto_ptr<_Ty>&_Y)//赋值运算符重载 { if(this!=&_Y)//判断自身赋值给自身 { if (_Ptr != _Y._Ptr)//判断两指针是否指向同一内存 { if (_Owns) //判断该对象是否有拥有权 delete _Ptr; //释放成员指针指向的内存空间 _Owns = _Y._Owns; //修改该指针的拥有权 } else if (_Y._Owns) _Owns = true; _Ptr = _Y.release(); //修改_Y的拥有权,并将_Y的_Ptr赋值给该成员_Ptr从而使该对象具有_Y._Ptr指向空间的拥有权 } return (*this); } _Ty& operator*() { return *_Ptr; } _Ty* operator->() { return _Ptr; } _Ty*release()const { ((auto_ptr<_Ty>*)this)->_Owns = false; return (_Ptr); } ~auto_ptr() { if (_Owns) delete _Ptr; } private: bool _Owns;//拥有权 _Ty *_Ptr; }; int main() { int *p = new int(10); auto_ptr<int> pl(p); cout << *pl << endl; Test *pc = new Test; auto_ptr<Test> temp(pc); temp->fun(); auto_ptr<int>pal = pl; cout << *pal << endl; auto_ptr<int>pt; pt = pal; cout << *pal << endl; cout << *pt << endl; return 0; }
auto_ptr(const auto_ptr<_Ty>&_Y):_Owns(_Y._Owns),_Ptr(_Y.release()){}//此处的const并非意味你不能更改auto_ptr所拥有的对象,而是意味你不能更改auto_ptr的拥有权。(通俗讲就是如果使用const auto_ptr作为参数,对新对象的任何赋值操作都将导致编译期错误。就常数特性而言,const auto_ptr比较类似常数指针(T*const p),而非指向常数的指针(const T*p)——尽管其语法看上去比较像后者。所以_Y.release()改变了_Y.Owns的值不影响函数运行。
1 _Ty*release()const 2 { 3 ((auto_ptr<_Ty>*)this)->_Owns = false; 4 return (_Ptr); 5 }
同时这里因为函数是const修饰的常函数所以不能对其进行修改,但是((auto_ptr<_Ty>*)this)其意义是将const (auto_ptr<_Ty>*)类型强制转换为(atuo_ptr<_Ty*>类型,这样就可以对_Owns进行修改,但是这只能在强制之时进行修改,一旦执行完强转其类型依然变为原来的const (auto_ptr<_Ty>*)类型,使其它方法无法修改其中数据。
运行结果 :
no memory leaks 无内存泄漏
这里有一些问题,为什么*pal的拥有权已经转移却还能输出呢?因为vc版的auot_ptr就是将拥有权转移后,除了不能够对其多次析构外,还可以对其进行操作,这就不好了,你都已经分手了,还不放手。在VS版本上对拥有权有更好的管理。