首页 > 代码库 > 【C++智能指针 auto_ptr】

【C++智能指针 auto_ptr】

《More Effective C++》ITEM M9中提到了auto_ptr,说是当异常产生的时候,怎么释放为对象分配的堆内存,避免重复编写内存释放语句。

PS:这里书里面提到函数退出问题,函数退出会清理栈内存,不管是怎么正常退出还是异常退出(仅有一种例外就是当你调用 longjmp 时。Longjmp 的这个缺点是 C++率先支持异常处理的主要原因)。建立在此基础上我们才把对指针的删除操作封装到一个栈对象里面。这样函数退出(异常或是正常)就会调用对象的析构函数,达到我们自动清理所封装指针指向的内存的目的。

作为新手,不是很理解,记下来,学习学习。

PS:C++11已经不提倡用auto_ptr了,请看链接:http://www.cplusplus.com/reference/memory/auto_ptr/

Note: This class template is deprecated as of C++11. unique_ptr is a new facility with a similar functionality, but with improved security (no fake copy assignments), added features (deleters) and support for arrays. Seeunique_ptr for additional information.


以下内容copy自:http://blog.sina.com.cn/s/blog_7708265a01010lyv.html

1. auto_ptr是什么?

auto_ptr 是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个拥有者。当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有的动态内存自动释放。即使发生异常,通过异常的栈展开过程也能将动态内存释放。auto_ptr不支持new 数组。

 

2. auto_ptr需要包含的头文件?

#include <memory>

 

3. 初始化auto_ptr对象的方法?

1) 构造函数

1] 将已存在的指向动态内存的普通指针作为参数来构造

int* p = new int(33);

auto_ptr<int> api(p);

2] 直接构造智能指针

auto_ptr< int > api( new int( 33 ) );

2) 拷贝构造

利用已经存在的智能指针来构造新的智能指针

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

auto_ptr< string > pstr_auto2( pstr_auto );  //利用pstr_auto来构造pstr_auto2

因为一块动态内存智能由一个智能指针独享,所以在拷贝构造或赋值时都会发生拥有权转移的过程。在此拷贝构造过程中,pstr_auto将失去对字符串内存的所有权,而pstr_auto2将其获得。对象销毁时,pstr_auto2负责内存的自动销毁。

3) 赋值

利用已经存在的智能指针来构造新的智能指针

auto_ptr< int > p1( new int( 1024 ) );

auto_ptr< int > p2( new int( 2048 ) );

p1 = p2;

在赋值之前,由p1 指向的对象被删除。赋值之后,p1 拥有int 型对象的所有权。该对象值为2048。 p2 不再被用来指向该对象。

 

4. 空的auto_ptr 需要初始化吗?

通常的指针在定义的时候若不指向任何对象,我们用Null给其赋值。对于智能指针,因为构造函数有默认值0,我们可以直接定义空的auto_ptr如下:

auto_ptr< int > p_auto_int;  //不指向任何对象

 

5. 防止两个auto_ptr对象拥有同一个对象(一块内存)

因为auto_ptr的所有权独有,所以下面的代码会造成混乱。

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

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

 

6. 警惕智能指针作为参数!

1) 按值传递时,函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的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已经不再拥有任何对象了。

2) 引用或指针时,不会存在上面的拷贝过程。但我们并不知道在函数中对传入的auto_ptr做了什么,如果当中某些操作使其失去了对对象的所有权,那么这还是可能会导致致命的执行期错误。

结论:const reference是智能指针作为参数传递的底线。

 

7. auto_ptr不能初始化为指向非动态内存

原因很简单,delete 表达式会被应用在不是动态分配的指针上这将导致未定义的程序行为。

 

8. auto_ptr常用的成员函数

1) get()

返回auto_ptr指向的那个对象的内存地址。如下例:

int* p = new int(33);

cout << "the adress of p: "<< p << endl;

auto_ptr<int> ap1(p);

cout << "the adress of ap1: " << &ap1 << endl;

cout << "the adress of the object which ap1 point to: " << ap1.get() << endl;

输出如下:

the adress of p: 00481E00

the adress of ap1: 0012FF68

the adress of the object which ap1 point to: 00481E00

第一行与第三行相同,都是int所在的那块内存的地址。第二行是ap1这个类对象本身所在内存的地址。

2) reset()

重新设置auto_ptr指向的对象。类似于赋值操作,但赋值操作不允许将一个普通指针指直接赋给auto_ptr,而reset()允许。如下例:

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

pstr_auto.reset( new string( "Long -neck" ) );

在例子中,重置前pstr_auto拥有"Brontosaurus"字符内存的所有权,这块内存首先会被释放。之后pstr_auto再拥有"Long -neck"字符内存的所有权。

注:reset(0)可以释放对象,销毁内存。

3) release()

返回auto_ptr指向的那个对象的内存地址,并释放对这个对象的所有权。

用此函数初始化auto_ptr时可以避免两个auto_ptr对象拥有同一个对象的情况(与get函数相比)。

例子如下:

auto_ptr< string > pstr_auto( new string( "Brontosaurus" ) );

auto_ptr< string > pstr_auto2( pstr_auto.get() ); //这是两个auto_ptr拥有同一个对象

auto_ptr< string > pstr_auto2( pstr_auto.release() ); //release可以首先释放所有权

附上auto_ptr的实现代码:

namespace std
{
 template<class T>
 class auto_ptr 
 {
 private:
  T* ap; 
 public:

  // constructor & destructor ----------------------------------- (1)
  explicit auto_ptr (T* ptr = 0) throw() : ap(ptr){}

  ~auto_ptr() throw() 
  {
   delete ap;
  }

  
  // Copy & assignment --------------------------------------------(2)
  auto_ptr (auto_ptr& rhs) throw() :ap(rhs.release()) {}
  template<class Y>  
  auto_ptr (auto_ptr<Y>& rhs) throw() : ap(rhs.release()) { }

  auto_ptr& operator= (auto_ptr& rhs) throw() 
  {
   reset(rhs.release());
   return *this;
  }
  template<class Y>
  auto_ptr& operator= (auto_ptr<Y>& rhs) throw() 
  {
   reset(rhs.release());
   return *this;
  }

  // Dereference----------------------------------------------------(3)
  T& operator*() const throw() 
  {
   return *ap;
  }
  T* operator->() const throw() 
  {
   return ap;
  }

  // Helper functions------------------------------------------------(4)
  // value access
  T* get() const throw() 
  {
   return ap;
  }

  // release ownership
  T* release() throw()
  {
   T* tmp(ap);
   ap = 0;
   return tmp;
  }

  // reset value
  void reset (T* ptr=0) throw() 
  {
   if (ap != ptr) 
   {
    delete ap;
    ap = ptr;
   }
  }

  // Special conversions-----------------------------------------------(5)
  template<class Y>
  struct auto_ptr_ref
  {
   Y* yp;
   auto_ptr_ref (Y* rhs) : yp(rhs) {}
  };

  auto_ptr(auto_ptr_ref<T> rhs) throw() : ap(rhs.yp) { }
  auto_ptr& operator= (auto_ptr_ref<T> rhs) throw() 
  {  
   reset(rhs.yp);
   return *this;
  }
  template<class Y>
  operator auto_ptr_ref<Y>() throw() 
  {
   return auto_ptr_ref<Y>(release());
  }
  template<class Y>
  operator auto_ptr<Y>() throw()
  {
   return auto_ptr<Y>(release());
  }
 };
}


【C++智能指针 auto_ptr】