C++中的auto_ptr智能指针

C++中的auto_ptr(俗称智能指针)所做的事情,使用起来就像普通指针,但当其动态分配内存时,不再需要考虑清理问题。当它的生存期结束时,系统会自动清理它指向的内存。

 

其实auto_ptr是一个模版类(注意实质上还是一个类)。主要解决内存泄漏问题。

 

原理:其实就是RAII,在构造的时候获取资源,在析构的时候释放资源,并进行相关指针操作的重载,使用起来就像普通的指针(!!!其实实质上还是一个类)。

 

0. 先看看如何使用:

[cpp]  view plain  copy
  1. int * a=new int(5);  
  2. auto_ptr<int> ap(a);//获取某个对象的所有权  
  3. cout<<(*ap)<<endl;//之后ap就可以像指针一样使用了。  

注意头文件是#include<memory>

注意是auto<int>而不是auto<int *>。观察源代码发现类auto_ptr的私有成员是T* ap;所以这里是<int>而不是<int*>。

 

最重要的就是理解源码:

[cpp]  view plain  copy
  1. template<class T>  
  2. class auto_ptr  
  3. {  
  4. private:  
  5. T*ap;  
  6. public:  
  7. //constructor&destructor-----------------------------------(1)  
  8. explicit auto_ptr(T*ptr=0)throw():ap(ptr){}//首先它的构造函数是不支持隐式转换的  
  9. ~auto_ptr()throw()//虽然构造函数中没有new空间,但是这里使用了delete。是因为智能指针是用来获取指针所有权的,而这个指针是指向new出来的空间的。所以析构需要delete  
  10. {  
  11. delete ap;  
  12. }  
  13. //Copy--------------------------------------------(2)  
  14. //它的copy有两个函数,注意两个copy函数内部实现,源智能指针将失去对指针的所有权。  
  15. auto_ptr(auto_ptr &rhs)throw():ap(rhs.release())//同类型的copy,例如:auto_ptr<int> a(p); auto_ptr<int> b(a);-->就是调用了这个copy  
  16. {}                                                
  17. template<class Y>  
  18. auto_ptr(auto_ptr<Y>& rhs)throw():ap(rhs.release())//模版中的类型不同的copy。例如:auto_ptr<Derived> a(p); auto_ptr<Base> b(a);用于多态中  
  19. {}  
  20. //assignment  
  21. //和copy类似,源智能指针将失去对指针的所有权。  
  22. auto_ptr& operator=(auto_ptr& rhs)throw()  
  23. {  
  24. reset(rhs.release());  
  25. return*this;  
  26. }  
  27. template<class Y>  
  28. auto_ptr& operator=(auto_ptr<Y>& rhs)throw()//参照上面的copy,用于多态中。  
  29. {  
  30. reset(rhs.release());  
  31. return*this;  
  32. }  
  33. //Dereference----------------------------------------------------(3)  
  34. //类auto_ptr重载了这两个操作符,使得它用起来像指针一样。  
  35. T&operator*()const throw()  
  36. {  
  37. return*ap;  
  38. }  
  39. T*operator->()const throw()  
  40. {  
  41. return ap;  
  42. }  
  43. //Helperfunctions------------------------------------------------(4)  
  44. //类auto_ptr实现了几个公共函数,提供用户使用  
  45. //valueaccess  
  46. T* get()const throw()//获取该智能指针拥有的指针  
  47. {  
  48. return ap;  
  49. }  
  50. //release ownership  
  51. T* release()throw()//释放该智能指针拥有的指针  
  52. {  
  53. T*tmp(ap);  
  54. ap=0;  
  55. return tmp;  
  56. }  
  57. //reset value  
  58. void reset(T*ptr=0)throw()//给该智能指针重新获取另一个指针的所有权  
  59. {  
  60. if(ap!=ptr)  
  61. {  
  62. delete ap;  
  63. ap=ptr;  
  64. }  
  65. }  
  66. //Special conversions-----------------------------------------------(5)  
  67. //存在的作用是可以使得下列的式子成立。  
  68. //auto_ptr<int> ap=auto_ptr<int>(new int(1));  
  69. //auto_ptr<int> ap;  
  70. //ap=auto_ptr<int> (new int(1));  
  71. template<class Y>  
  72. struct auto_ptr_ref  
  73. {  
  74. Y* yp;  
  75. auto_ptr_ref(Y*rhs):yp(rhs){}  
  76. };  
  77.   
  78. auto_ptr(auto_ptr_ref<T> rhs)throw():ap(rhs.yp)  
  79. {}  
  80. auto_ptr& operator=(auto_ptr_ref<T> rhs)throw()  
  81. {  
  82. reset(rhs.yp);  
  83. return*this;  
  84. }  
  85. template<class Y>  
  86. operator auto_ptr_ref<Y>()throw()  
  87. {  
  88. return auto_ptr_ref<Y>(release());  
  89. }  
  90. template<class Y>  
  91. operator auto_ptr<Y>()throw()  
  92. {  
  93. return auto_ptr<Y>(release());  
  94. }  
  95. };  


注意的方面:

1. 看看构造函数与析构函数

1> auto的构造时获得对某个对象的所有权。

[cpp]  view plain  copy
  1. class A  
  2. {  
  3. public: A(int i):m_a(i)  
  4.               {}  
  5.         int m_a;  
  6. };  
  7. int _tmain(int argc, _TCHAR* argv[])  
  8. {  
  9.        int * a=new int(5);  
  10.        A*a=new A(3);  
  11.        auto_ptr<int> ap(a);  
  12.        //auto_ptr<int> ap=a;   -----(1) error  
  13.        auto_ptr<A>apA=auto_ptr<A>(new A(2));  
  14.        //auto_ptr<A> apA=new A(2);  ------(2)         error  
  15.        //auto_ptr<A> apA=(auto_ptr<A>)a; ------(3)    error  
  16.        system("pause");  
  17.        return 0;  
  18. }  

注意:

        前两个错误在于调用了类auto_ptr的copy构造,而很显然没有匹配的copy构造。其实就相当于两个不相干的类的对象进行copy构造,这样显然是不对的。

         3错误在于调用了类的赋值重载,其实就相当于两个不相干的类的对象进行赋值操作,这样显然很荒谬。

2>  因为auto_ptr析构会删除内存。所以不要两个auto_ptr拥有同一对象:

[cpp]  view plain  copy
  1. int * a=new int(5);  
  2. auto_ptr<int> ap1(a);  
  3. auto_ptr<int> ap2(a);  

这样是很危险。

3> 因为auto_ptr的析构中删除指针用的是delete,而不是delete[].所以我们不能用auto_ptr来管理一个类对象数组。

 

2. 拷贝和赋值函数

1> 由源码可知,auto_ptr的拷贝和赋值后,源智能指针将失去对指针的所有权。

[cpp]  view plain  copy
  1. int * p=new int(2);  
  2. auto_ptr<int> a(p);  
  3. auto_ptr<int> b(a);  
  4. //cout<<*a<<endl;    error  

2> 由于拷贝和赋值的特殊性,当智能指针作为参数对函数进行传参时,就会出现问题,传参的过程中,系统会自动生成一个临时智能指针,实参对其copy构造,则实参就失去了所有权,而函数结束后,系统会销毁这个临时智能指针,所以连同绑定的指针指向的内存都已经被释放,显然不合理。

所以不要把智能指针当作参数。

3> 有源码中可以看到copy和赋值都有两个函数,而第二个函数就是用于多态的。用子类指针对父类指针进行copy构造和赋值。

4> auto_ptr不能作为STL中的容器对象。因为STL容器中的元素要经常copy和赋值,而auto_ptr会传递所有权,不是值语义的。所以不行。

附:值语义就相当于值传递;对象语义就相当于引用传递。

 3. auto_ptr提供了一些函数可供使用

get();  release();reset();

 4. 说一下特殊转换

//auto_ptr<int> ap=auto_ptr<int>(new int(1));

//auto_ptr<int> ap;

//ap=auto_ptr<int> (new int(1));

如果想使得这两个式子成立,必须要有auto_ptr_ref。

首先:

//auto_ptr<int> ap=auto_ptr<int>(new int(1));

因为auto_ptr<int>(newint(1))是一个临时对象,而临时对象是不能作为copy构造里的引用参数的,所以我们必须重写一个copy构造,使得copy构造的参数不是引用类型。

而我们不能写出这样的copy:auto_ptr(auto_ptr p){};这样显然是不行的,因为传参就会调用copy,则它就会循环调用。所以我们写成:

[cpp]  view plain  copy
  1. auto_ptr(auto_ptr_ref<T> rhs)throw():ap(rhs.yp)  
  2. {}  

之后,我们就要把auto_ptr可以转换成auto_ptr_ref,所以我们写了一个类型转换函数:

[cpp]  view plain  copy
  1. template<class Y>  
  2. operatorauto_ptr_ref<Y>()throw()  
  3. {  
  4. returnauto_ptr_ref<Y>(release());  
  5. }  

这样,我们就可以把那个临时对象转换成auto_ptr_ref,然后再调用我们重写的这个copy构造,我们就实现了这个copy过程。

 

 

//auto_ptr<int> ap;

//ap=auto_ptr<int> (new int(1));

而赋值过程和上面的过程是类似的。我们重写赋值函数就可以了。

至此我们用掉用了3个函数。

而auto_ptr_ref里面有4个函数。还有一个:

[cpp]  view plain  copy
  1. template<class Y>  
  2. operatorauto_ptr<Y>()throw()  
  3. {  
  4. returnauto_ptr<Y>(release());  
  5. }  
  6. };  

而这个类型转换函数的作用:就是不同模版类型可以相互转换。比如:

[cpp]  view plain  copy
  1. template<typename T>  
  2. class Base  
  3. {  
  4. public:  
  5.        template<typenameY>  
  6.        operator Base<Y>(){  
  7.               return Base<Y>();  
  8.        }  
  9. };  
  10.    
  11. int _tmain(int argc, _TCHAR* argv[])  
  12. {  
  13.        Base<int> a;  
  14.        Base<short> b;  
  15.        a=b;//OK  
  16.        system("pause");  
  17.        return 0;  
  18. }  

使得a=b;可行。但是具体在auto_ptr中,这个函数具体怎么用,还没有遇到这种情况。

 

心得:

其实这个auto_ptr就是一个类,只是重载了*和->符号,所以它的对象可以像指针一样:*p来返回引用对象。p->来调用对象的成员。需要注意的是类auto_ptr的实现(重点),比如copy和赋值都跟常理不同。而因为本质上是一个类,所以销毁它是就自动调用了析构函数,使得不会因为异常而内存泄漏。就是RAII技术。

猜你喜欢

转载自blog.csdn.net/wangjingqi930330/article/details/80329273