关于auto_ptr

来源:互联网 发布:fkled编辑软件下载 编辑:程序博客网 时间:2024/06/03 05:41

       智能指针auto_ptr 在我理解是用一个对象来管理原生指针,通过拥有权Owns的控制,在智能指针对象析构时来释放原生指针的空间,以此来保证内存总是被释放掉,避免内存泄露。

<span style="font-size:14px;">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;};</span>

拷贝构造函数
<span style="font-size:14px;">auto_ptr(const auto_ptr<_Ty>& _Y) _THROW0(): _Owns(_Y._Owns), _Ptr(_Y.release()) {}</span>

当用一个智能指针拷贝构造另一个智能指针时,要考虑,如果在构造新的对象时只进行“浅拷贝”
_Owns=_Y._Owns;
_Ptr=_Y._Ptr;
这样会造成两个只能指针同时指向同一块内存且同时有拥有权,在这两个对象析构时会对同一块内存释放两次引发错误。

在成员初始化类表中先对_Owns赋值之后再对_Ptr赋值 并同时调动release()函数将_Y._Owns赋值为false,如果Y有拥有权,那么新对象将获得拥有权,然后Y失去拥有权,这样就避免两个对象对同一块内存都有拥有权的错误。但如果写成这样
<span style="font-size:14px;">auto_ptr(const auto_ptr<_Ty>& _Y) _THROW0():<span style="font-family: Arial, Helvetica, sans-serif;">_Ptr(_Y.release())</span>,_Owns(_Y._Owns),  {}</span>
看起来是Y先失去拥有权,再对_Owns赋值,似乎_Owns总是false.但在我自己写这个auto_ptr时确实把拷贝构造写成了上面的样子,但_Owns仍能被正确赋值。

也就是说_Owns(_Y._Owns)总是在_Ptr(_Y.release())前执行,因为成员初始化的顺序只与成员声明顺序有关与其在初始化列表中的顺序无关。
<span style="font-size:14px;">private:bool _Owns;_Ty *_Ptr;</span>
_Owns的初始化总早于_Ptr

赋值构造
<span style="font-size:14px;">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); }</span>
当用一个智能指针对象赋值另一个智能指针对象时同样要考虑_Owns的问题,不能两个对象对同一块内存都有拥有权,同时还要考虑被修改的这个指针原来管理的那块内存要释放掉。但源码中这种写法有一个小问题
int *p = new int(3);

auto_ptr<int> p2(p);
auto_ptr<int> p3(p2);//p2._Owns为false
auto_ptr<int> p4(p);
p4=p2;//////////////////////////在这用一个对p所指向空间没有拥有权的p2来给p4赋值,最后得到结果p4却是对此空间有拥有权的。。。  但如果p4也被赋值成无拥有权,那么又会有内存泄露,然后再想,那在被赋值前先释放掉那块内存好了,免得发生泄露,但这样做的结果是  经过这样的以此赋值,两个只能指针都指向了一块已经被释放的内存。源码这样写还是有他的道理的。

释放函数

<span style="font-size:14px;">_Ty *release() const _THROW0(){((auto_ptr<_Ty> *)this)->_Owns = false;return (_Ptr); }</span>

释放拥有权并返回指针
<span style="font-size:14px;">((auto_ptr<_Ty> *)this)->_Owns = false;</span>
那这语句为什么要进行强制类型转换,是为了去掉this指针的常性,从而能再const修饰的成员函数中修改_Owns
那既然要修改_Owns直接去掉const就好了 不用这么麻烦 。网上看会发现 在拷贝构造函数和赋值构造函数中都调用了_Y.release(),并且为了安全性考虑 参数_Y是一个常引用方式传进来的,所以_Y只能调用常方法,release()const  这个const是不能去掉的。
这时就需要在常方法中来改变成员变量,上边的强制类型转换是一种方法还可以通过关键字mutable来实现,mutalbe的意思是“可变的”,跟const是反义词。mutable是为了突破const的限制而设置的。被mutable修饰的变量将永远处于可变的状态,即使在一个const函数中
private:
mutable bool  _Owns;
_Ty *_Ptr;

0 0
原创粉丝点击