C++标准模板库中的auto_ptr

http://blog.csdn.net/wcyoot/article/details/6546375

标准模板库源代码:

  1. namespace std  
  2. {  
  3.     // 特殊类,用于non-const auto_ptrs的拷贝和赋值  
  4.     // 由于auto_ptr的拷贝构造、赋值运算都需要使用引用传递参数(否则会照成指针所有权并没有真正移交)  
  5.     // 因此需要支持当临时右值作为拷贝和赋值的功能  
  6.     // auto_ptr_ref设计目的:用来实现上述情形,如:  
  7.     // auto_ptr<int> ap1 = auto_ptr<int>(new int(8));//等号右边的是一个临时右值  
  8.     // auto_ptr<int> fun()//一个生成auto_ptr<int>的source函数  
  9.     // auto_ptr<int> ap2 ( fun() );//调用fun()生成的auto_ptr<int>是临时右值  
  10.     // 即支持将auto_ptr作为临时右值使用。  
  11.     template<class _Ty>  
  12.     struct auto_ptr_ref  
  13.     {   // proxy reference for auto_ptr copying  
  14.         explicit auto_ptr_ref(_Ty *_Right)  // explicit 说明该构造函数不能进行隐式类型转换  
  15.             : _Ref(_Right)  
  16.         {   // construct from generic pointer to auto_ptr ptr  
  17.         }  
  18.         _Ty *_Ref;  // generic pointer to auto_ptr ptr  
  19.     };  
  20.     template<class _Ty>  
  21.     class auto_ptr  
  22.     {   // wrap an object pointer to ensure destruction  
  23.     public:  
  24.         typedef _Ty element_type;  
  25.         // 显式构造函数,不支持从普通ptr到auto_ptr的隐式转换,确保其安全性  
  26.         explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()  
  27.             : _Myptr(_Ptr)  
  28.         {   // construct from object pointer  
  29.         }  
  30.         // 构造时获取对某个对象的所有权(ownership),在析构时释放该对象。  
  31.         auto_ptr(auto_ptr<_Ty>& _Right) _THROW0()  
  32.             : _Myptr(_Right.release())  
  33.         {   // construct by assuming pointer from _Right auto_ptr  
  34.         }  
  35.         // 构造时获取对某个对象的所有权(ownership),在析构时释放该对象。  
  36.         // 注意:此处是值传递,所以可以用来传入右值  
  37.         auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()  
  38.         {   // construct by assuming pointer from _Right auto_ptr_ref  
  39.             _Ty *_Ptr = _Right._Ref;  
  40.             _Right._Ref = 0;    // release old  
  41.             _Myptr = _Ptr;  // reset this  
  42.         }  
  43.         // 类型转换,使得类的继承关系得以支持,如果两个类型不能进行转换,编译时将会得到警告或错误  
  44.         template<class _Other>  
  45.         operator auto_ptr<_Other>() _THROW0()  
  46.         {   // convert to compatible auto_ptr  
  47.             return (auto_ptr<_Other>(*this));  
  48.         }  
  49.         // 类型转换,使得类的继承关系得以支持  
  50.         template<class _Other>  
  51.         operator auto_ptr_ref<_Other>() _THROW0()  
  52.         {   // convert to compatible auto_ptr_ref  
  53.             _Other *_Cvtptr = _Myptr;   // test implicit conversion  
  54.             auto_ptr_ref<_Other> _Ans(_Cvtptr);  
  55.             _Myptr = 0; // pass ownership to auto_ptr_ref  
  56.             return (_Ans);  
  57.         }  
  58.         // 带类型转换的赋值运算符重载,可能需要释放当前资源  
  59.         template<class _Other>  
  60.         auto_ptr<_Ty>& operator=(auto_ptr<_Other>& _Right) _THROW0()  
  61.         {   // assign compatible _Right (assume pointer)  
  62.             reset(_Right.release());  
  63.             return (*this);  
  64.         }  
  65.         // 拷贝构造函数,转移指针所有权  
  66.         template<class _Other>  
  67.         auto_ptr(auto_ptr<_Other>& _Right) _THROW0()  
  68.             : _Myptr(_Right.release())  
  69.         {   // construct by assuming pointer from _Right  
  70.         }  
  71.         // 赋值运算符重载  
  72.         auto_ptr<_Ty>& operator=(auto_ptr<_Ty>& _Right) _THROW0()  
  73.         {   // assign compatible _Right (assume pointer)  
  74.             reset(_Right.release());  
  75.             return (*this);  
  76.         }  
  77.         // 赋值运算符重载  
  78.         auto_ptr<_Ty>& operator=(auto_ptr_ref<_Ty> _Right) _THROW0()  
  79.         {   // assign compatible _Right._Ref (assume pointer)  
  80.             _Ty *_Ptr = _Right._Ref;  
  81.             _Right._Ref = 0;    // release old  
  82.             reset(_Ptr);    // set new  
  83.             return (*this);  
  84.         }  
  85.         // 析构时释放资源,它是安全的  
  86.         ~auto_ptr()  
  87.         {   // destroy the object  
  88.             if (_Myptr != 0)  
  89.                 delete _Myptr;  
  90.         }  
  91.         // 基本操作:*运算符  
  92.         _Ty& operator*() const _THROW0()  
  93.         {   // return designated value  
  94. #if _HAS_ITERATOR_DEBUGGING  
  95.             if (_Myptr == 0)  
  96.                 _DEBUG_ERROR("auto_ptr not dereferencable");  
  97. #endif /* _HAS_ITERATOR_DEBUGGING */  
  98.             __analysis_assume(_Myptr);  
  99.             return (*get());  
  100.         }  
  101.         // 基本操作:->运算符  
  102.         _Ty *operator->() const _THROW0()  
  103.         {   // return pointer to class object  
  104. #if _HAS_ITERATOR_DEBUGGING  
  105.             if (_Myptr == 0)  
  106.                 _DEBUG_ERROR("auto_ptr not dereferencable");  
  107. #endif /* _HAS_ITERATOR_DEBUGGING */  
  108.             return (get());  
  109.         }  
  110.         // 辅助函数:显式的返回auto_ptr所拥有的对象指针  
  111.         _Ty *get() const _THROW0()  
  112.         {   // return wrapped pointer  
  113.             return (_Myptr);  
  114.         }  
  115.         // 辅助函数:用来转移所有权  
  116.         _Ty *release() _THROW0()  
  117.         {   // return wrapped pointer and give up ownership  
  118.             _Ty *_Tmp = _Myptr;  
  119.             _Myptr = 0;  
  120.             return (_Tmp);  
  121.         }  
  122.         // 辅助函数:用来接收所有权  
  123.         void reset(_Ty* _Ptr = 0)  
  124.         {   // destroy designated object and store new pointer  
  125.             if (_Ptr != _Myptr && _Myptr != 0)  
  126.                 delete _Myptr;  
  127.             _Myptr = _Ptr;  
  128.         }  
  129.     private:  
  130.         _Ty *_Myptr;    // the wrapped object pointer  
  131.     };  
  132. }  
 

 

1、 构造函数与析构函数

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

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

    从此我们不必关心应该何时释放p,也不用担心发生异常会有内存泄漏。
    这里我们有几点要注意:
1) 因为auto_ptr析构的时候肯定会删除他所拥有的那个对象,因此两个auto_ptr不能同时拥有同一个对象。像这样:
  1. int* p = new int(0);  
  2. auto_ptr<int> ap1(p);  
  3. auto_ptr<int> ap2(p);// 危险!!p将会被释放两次  

      因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用auto_ptr.
2) 考虑下面这种用法:
  1. int* pa = new int[10];  
  2. 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)操作是不安全的。

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

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

  1. void f(auto_ptr<int> ap){cout<<*ap;}  
  2. auto_ptr<int> ap1(new int(0));  
  3. f(ap1);  
  4. 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的目的:用来实现上述情形,如:

  1. auto_ptr<int> ap1 = auto_ptr<int>(new int(8));//等号右边的是一个临时右值  
  2. auto_ptr<int> fun()//一个生成auto_ptr<int>的source函数  
  3. 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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值