C++标准模板库中的auto_ptr

来源:互联网 发布:win10和mac os 编辑:程序博客网 时间:2024/06/01 10:19

原帖地址 : http://blog.csdn.net/wcyoot/article/details/6546375


标准模板库源代码:

namespace std{// 特殊类,用于non-const auto_ptrs的拷贝和赋值// 由于auto_ptr的拷贝构造、赋值运算都需要使用引用传递参数(否则会照成指针所有权并没有真正移交)// 因此需要支持当临时右值作为拷贝和赋值的功能// auto_ptr_ref设计目的:用来实现上述情形,如:// auto_ptr<int> ap1 = auto_ptr<int>(new int(8));//等号右边的是一个临时右值// auto_ptr<int> fun()//一个生成auto_ptr<int>的source函数// auto_ptr<int> ap2 ( fun() );//调用fun()生成的auto_ptr<int>是临时右值// 即支持将auto_ptr作为临时右值使用。template<class _Ty>struct auto_ptr_ref{// proxy reference for auto_ptr copyingexplicit auto_ptr_ref(_Ty *_Right)// explicit 说明该构造函数不能进行隐式类型转换: _Ref(_Right){// construct from generic pointer to auto_ptr ptr}_Ty *_Ref;// generic pointer to auto_ptr ptr};template<class _Ty>class auto_ptr{// wrap an object pointer to ensure destructionpublic:typedef _Ty element_type;// 显式构造函数,不支持从普通ptr到auto_ptr的隐式转换,确保其安全性explicit auto_ptr(_Ty *_Ptr = 0) _THROW0(): _Myptr(_Ptr){// construct from object pointer}// 构造时获取对某个对象的所有权(ownership),在析构时释放该对象。auto_ptr(auto_ptr<_Ty>& _Right) _THROW0(): _Myptr(_Right.release()){// construct by assuming pointer from _Right auto_ptr}// 构造时获取对某个对象的所有权(ownership),在析构时释放该对象。// 注意:此处是值传递,所以可以用来传入右值auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0(){// construct by assuming pointer from _Right auto_ptr_ref_Ty *_Ptr = _Right._Ref;_Right._Ref = 0;// release old_Myptr = _Ptr;// reset this}// 类型转换,使得类的继承关系得以支持,如果两个类型不能进行转换,编译时将会得到警告或错误template<class _Other>operator auto_ptr<_Other>() _THROW0(){// convert to compatible auto_ptrreturn (auto_ptr<_Other>(*this));}// 类型转换,使得类的继承关系得以支持template<class _Other>operator auto_ptr_ref<_Other>() _THROW0(){// convert to compatible auto_ptr_ref_Other *_Cvtptr = _Myptr;// test implicit conversionauto_ptr_ref<_Other> _Ans(_Cvtptr);_Myptr = 0;// pass ownership to auto_ptr_refreturn (_Ans);}// 带类型转换的赋值运算符重载,可能需要释放当前资源template<class _Other>auto_ptr<_Ty>& operator=(auto_ptr<_Other>& _Right) _THROW0(){// assign compatible _Right (assume pointer)reset(_Right.release());return (*this);}// 拷贝构造函数,转移指针所有权template<class _Other>auto_ptr(auto_ptr<_Other>& _Right) _THROW0(): _Myptr(_Right.release()){// construct by assuming pointer from _Right}// 赋值运算符重载auto_ptr<_Ty>& operator=(auto_ptr<_Ty>& _Right) _THROW0(){// assign compatible _Right (assume pointer)reset(_Right.release());return (*this);}// 赋值运算符重载auto_ptr<_Ty>& operator=(auto_ptr_ref<_Ty> _Right) _THROW0(){// assign compatible _Right._Ref (assume pointer)_Ty *_Ptr = _Right._Ref;_Right._Ref = 0;// release oldreset(_Ptr);// set newreturn (*this);}// 析构时释放资源,它是安全的~auto_ptr(){// destroy the objectif (_Myptr != 0)delete _Myptr;}// 基本操作:*运算符_Ty& operator*() const _THROW0(){// return designated value#if _HAS_ITERATOR_DEBUGGINGif (_Myptr == 0)_DEBUG_ERROR("auto_ptr not dereferencable");#endif /* _HAS_ITERATOR_DEBUGGING */__analysis_assume(_Myptr);return (*get());}// 基本操作:->运算符_Ty *operator->() const _THROW0(){// return pointer to class object#if _HAS_ITERATOR_DEBUGGINGif (_Myptr == 0)_DEBUG_ERROR("auto_ptr not dereferencable");#endif /* _HAS_ITERATOR_DEBUGGING */return (get());}// 辅助函数:显式的返回auto_ptr所拥有的对象指针_Ty *get() const _THROW0(){// return wrapped pointerreturn (_Myptr);}// 辅助函数:用来转移所有权_Ty *release() _THROW0(){// return wrapped pointer and give up ownership_Ty *_Tmp = _Myptr;_Myptr = 0;return (_Tmp);}// 辅助函数:用来接收所有权void reset(_Ty* _Ptr = 0){// destroy designated object and store new pointerif (_Ptr != _Myptr && _Myptr != 0)delete _Myptr;_Myptr = _Ptr;}private:_Ty *_Myptr;// the wrapped object pointer};}


 

1、 构造函数与析构函数

auto_ptr在构造时获取对某个对象的所有权(ownership),在析构时释放该对象。我们可以这样使用auto_ptr来提高代码安全性:

int* p = new int(0);auto_ptr<int> ap(p);


    从此我们不必关心应该何时释放p,也不用担心发生异常会有内存泄漏。
    这里我们有几点要注意:

1) 因为auto_ptr析构的时候肯定会删除他所拥有的那个对象,因此两个auto_ptr不能同时拥有同一个对象。像这样:

int* p = new int(0);auto_ptr<int> ap1(p);auto_ptr<int> ap2(p);// 危险!!p将会被释放两次


      因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用auto_ptr.

2) 考虑下面这种用法:

int* pa = new int[10];auto_ptr<int> ap(pa);  // 危险!!auto_ptr中删除指针用的是delete而不是delete[]


    因为auto_ptr的析构函数中删除指针用的是delete,而不是delete [],所以我们不应该用auto_ptr来管理一个数组指针。
3) 构造函数的explicit关键词有效阻止从一个“裸”指针隐式转换成auto_ptr类型。
4) C++保证删除一个空指针是安全的,析构函数是安全的。

 

2、拷贝构造与赋值

     与引用计数型智能指针不同的,auto_ptr要求其对“裸”指针的完全占有性。也就是说一个”裸“指针不能同时被两个以上的auto_ptr所拥有。那么,在拷贝构造或赋值操作时,我们必须作特殊的处理来保证这个特性。auto_ptr的做法是“所有权转移”,即拷贝或赋值的源对象将失去对“裸”指针的所有权,所以auto_ptr的拷贝构造函数,赋值函数的参数为引用而不是常引用(const reference)。当然,一个auto_ptr也不能同时拥有两个以上的“裸”指针,所以,拷贝或赋值的目标对象将先释放其原来所拥有的对象。

     这里的注意点是:

     1) 因为一个auto_ptr被拷贝或被赋值后, 其已经失去对原对象的所有权,这个时候,对这个auto_ptr的提领(dereference)操作是不安全的。

int* p = new int(0);auto_ptr<int> ap1(p);auto_ptr<int> ap2 = ap1;cout<<*ap1; //错误,此时ap1只剩一个null指针在手了


      这种情况较为隐蔽的情形出现在将auto_ptr作为函数参数按值传递,因为在函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的auto_ptr(拷贝构造),这样,传入的实参auto_ptr就失去了其对原对象的所有权,而该对象会在函数退出时被局部auto_ptr删除。如下:

void f(auto_ptr<int> ap){cout<<*ap;}auto_ptr<int> ap1(new int(0));f(ap1);cout<<*ap1; //错误,经过f(ap1)函数调用,ap1已经不再拥有任何对象了。


     因此,auto_ptr作为函数参数按值传递是一定要避免的。或许大家会想到用auto_ptr的指针或引用作为函数参数或许可以,但是仔细想想,我们并不知道在函数中对传入的auto_ptr做了什么, 如果当中某些操作使其失去了对对象的所有权, 那么这还是可能会导致致命的执行期错误。也许,用const reference的形式来传递auto_ptr会是一个不错的选择。

 

    2)我们可以看到拷贝构造函数与赋值函数都提供了一个成员模板在不覆盖“正统”版本的情况下实现auto_ptr的隐式转换。因此可以实现继承类指针到基类指针的转换。

 

    3)因为auto_ptr不具有值语义(value semantic), 所以auto_ptr不能被用在stl标准容器中。

所谓值语义,是指符合以下条件的类型(假设有类A):

A a1;
A a2(a1);
A a3;
a3 = a1;
那么
a2 == a1, a3 == a1
很明显,auto_ptr不符合上述条件,而我们知道stl标准容器要用到大量的拷贝赋值操作,并且假设其操作的类型必须符合以上条件。

 

3、 提领操作(dereference)

即“*运算符”和“->运算符”。首先要确保这个智能指针确实拥有某个对象,否则,这个操作的行为即对空指针的提领是未定义的。

 

4、辅助函数

1) get用来显式的返回auto_ptr所拥有的对象指针。标准库提供的auto_ptr既不提供从“裸”指针到auto_ptr的隐式转换(构造函数为explicit),也不提供从auto_ptr到“裸”指针的隐式转换,从使用上来讲可能不那么的灵活, 考虑到其所带来的安全性还是值得的。
2) release,用来转移所有权
3) reset,用来接收所有权,如果接收所有权的auto_ptr如果已经拥有某对象,必须先释放该对象。

5、特殊转换--auto_ptr_ref类相关

 

由于auto_ptr的拷贝构造、赋值运算都需要使用引用传递参数(否则会照成指针所有权并没有真正移交)

因此需要支持当临时右值作为拷贝和赋值的功能

设计auto_ptr_ref的目的:用来实现上述情形,如:


auto_ptr<int> ap1 = auto_ptr<int>(new int(8));//等号右边的是一个临时右值auto_ptr<int> fun()//一个生成auto_ptr<int>的source函数auto_ptr<int> ap2 ( fun() );//调用fun()生成的auto_ptr<int>是临时右值


引入auto_ptr_ref来实现从右值向左值的转换。其过程为:

1)auto_ptr<int>(new int(8))调用构造函数生成一个临时的auto_ptr对象

2)编译器发现ap1的构造函数无法匹配参数

3)发现辅助的构造函数auto_ptr(auto_ptr_ref<_Ty> _Right) ,调用隐式类型转换auto_ptr_ref()将该临时对象隐式转换为一个auto_ptr_ref对象

4)调用辅助构造函数auto_ptr(auto_ptr_ref<_Ty> _Right) 生成ap1


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 开发游戏平台给了钱不给东西怎么办 代号英雄与服务器断开连接了怎么办 千牛聊天页面买家信息不显示怎么办 秒拍存草稿箱的视频没了怎么办? 登录山东掌厅出现服务器错误怎么办 微信号被多人投诉被限制登录怎么办 联想平板电脑开机密码忘记了怎么办 申请的qq没登录忘了账号怎么办 炫舞时代由于网络原因登不上怎么办 qq申请太多进不了热聊怎么办 手机号申请的微信号被盗了怎么办 买菜别人少找了钱不还怎么办 在掌上英雄联盟买皮肤买错区怎么办 win8我的电脑图标没了怎么办 英雄联盟老是卡在安全扫描怎么办 英雄联盟活动送皮肤没送怎么办 电脑换完系统有些页面打不开怎么办 王卡助手交手机费页面打不开怎么办 在浏览器上打不开路由器页面怎么办 英雄联盟读条的时候自动关机怎么办 手机的位置信息开不了怎么办呢 滴滴车主接到乘客返回路程要怎么办 移动换话费积分是发什么短信怎么办 手机店积分换手机被贷款怎么办 心悦俱乐部礼包已过期是怎么办 心悦兑换的东西不是账号绑定怎么办 心悦会员绑定的手机号不用了怎么办 想在京东商城开个网店怎么办呢 京东买了东西收货了不想要了怎么办 京东转卖的商品有问题怎么办 如果衣服下架了然后有退货怎么办 想买二手车可没有懂车的人怎么办 买车的时候异地车牌回家怎么办 天猫下单显示下单人数太多券怎么办 英雄联盟进入游戏后无限崩溃怎么办 打开电视显示百度影棒打不开怎么办 家里路由器网速一会快一会慢怎么办 用快看影视下载电影网速太慢怎么办 苹果手机下载东西网速特别慢怎么办 网上买重庆时时彩输了很多钱怎么办 找不到自己在哪个平台借过钱怎么办