C++——auto_ptr类

http://blog.csdn.net/shineHoo/article/details/5195569

auto_ptr 类是一个模板类,它被定义在 memory 头文件中。
auto_ptr 类可以用于管理由 new 分配的单个对象,但是无法管理动态分配的数组(我们通常不会使用数组,而是使用 vector 代替数组)。auto_ptr 在拷贝和赋值的时候有不寻常的行为,因此 auto_ptrs 不能被保存在 stl 的容器中。当 auto_ptr 离开了自己的作用域或者被销毁,由 auto_ptr 管理的对象也会被销毁。

1. 为异常安全的内存分配(Exception-Safe Memory Allocation)使用 auto_ptr
我们看下面的代码:
void Func()
{
  int *pNum = new int(100);
  // code that throws an exception this is not caught inside Func
  delete pNum;
}
我们可以看到,假定在 new 和 delete 之间抛出了一个未捕获的异常,那么内存泄漏发生了,delete pNum 未被执行。这就是非异常安全的内存分配。同样还有一个类似的例子:
void Func()
{
  CMember* pMember = new CMember;
  CTeam* pTeam = QueryTeam(idTeam);
  if (NULL == pTeam)
  return; // Memory leaked
  pTeam->Add(pMember);
}
我们可以看到在动态分配了一个 CMember 对象后,由于出现某种情况函数 return,而 CMember 对象却没有得到释放。
假如我们使用 auto_ptr 类代替,那么内存将会被自动释放:
void Func()
{
  auto_ptr<int> ap(new int(100));
  // …
}


2. 绑定 auto_ptr 到一个指针:
通常的情况,我们这样使用 auto_ptr 类:
auto_ptr<CClass> pObject(new CClass);
这样,我们就完成了 auto_ptr 到指针的绑定。注意,以下这种表达是错误的:
auto_ptr<CClass> pObject = new CClass;
当 auto_ptr 离开自己的作用域时,绑定在 auto_ptr 上的指针指向的对象将被释放。

3. 使用 auto_ptr
auto_ptr 类重载了解引用操作符(*)和箭头操作符(->),这样,我们能够想使用内置指针一样的使用 auto_ptr,例如:
class CClass
{
public:
  void Print();
  // …
}
int main()
{
  std::auto_ptr<CClass> pObj(new CClass);
  pObj->Print(); // the same (*pObj).Print();
}


4. auto_ptr 的赋值和拷贝操作符
内置的指针和 auto_ptr 的区别在于赋值和拷贝,这个需要特别注意。如果是内置指针的拷贝(赋值),那么结果是这两个指针指向同一个对象,而 auto_ptr 则不一样:
auto_ptr<string> ap1(new string("Stegosaurus");
auto_ptr<string> ap2(ap1);
执行完上面的语句后,ap1 将处于非绑定(unbound)状态,并不指向任何对象,被管理的指针的所有权从 ap1 转到 ap2,即指针将被 ap2 管理(包括内存的释放等),而不是 ap1。

std::auto_ptr<std::string> ap1(new std::string("Pterodactry"));
std::auto_ptr<std::string> ap2;

如果是:ap2 = ap1, 则ap1指向NULL, ap2指向ap1所指的内容
如果是:ap1 = ap2; //ap1和ap2均指向NULL,注意此时所有资源已经被释放。 
正是因为拷贝和赋值会破坏右操作数,所以 auto_ptrs 无法存储在stl 的容器中。stl 的容器需要两个对象在拷贝或者赋值之后相等。

 

5. auto_ptr 类的函数
1)默认构造函数
auto_ptr<int> pNum; // 没有指向任何对象
*pNum = 100; // 错误
2)get 函数,返回指向对象的指针
if (NULL == pNum.get()) // 指针是否为空
注意,对于处于非绑定状态的 auto_ptr 调用 get 将返回 0。不要做这样的操作:
auto_ptr<int> pNum2(pNum1.get()); // 没有转交所有权
那么 pNum1 管理的指针将被析构两次,这必然带来严重的后果。

例如:

    auto_ptr<int> p1(new int(3));
    auto_ptr<int> p2(p1.get());

资源将被释放两次,会有严重问题。


3)reset 函数,重设需要管理的指针,首先 auto_ptr 会删除当前管理的对象,然后再设置新的对象的指针。另外:
pNum = pNum; // 不会发送任何事情,安全
pNum.reset(pNum->get()); // 不会发送任何事情,安全。http://blog.csdn.net/sunmenggmail/article/details/8178984

//这是因为在reset中有判断,内部指针是否等于外边指针。


4)release 函数释放所有权,返回它管理的指针,不删除指针指向的对象:
auto_ptr<int> pNum2(pNum1.release()); // 等同于下句。
auto_ptr<int> pNum2(pNum1); // 等同于上句


6. 编译器的问题
vc6 下的 auto_ptr 并不是标准的,例如:
std::auto_ptr<std::string> ap1(new std::string("Pterodactry"));
std::auto_ptr<std::string> ap2(ap1);
std::cout << *ap2 << std::endl; // 正常执行


7. 被管理的类的析构函数
如果被管理的类的析构函数为私有的,那么将无法使用 auto_ptr。 

 

补充: 

 

 

1.auto_ptr只能用于防止资源泄漏.也就是说是用auto_ptr的唯一目的在于使一个对象必然被delete.无论发生什么情况,这个对像都将在某个时候被释放,而不会泄漏.所以,下面直接将auto_ptr所控制的对象成为资源.

2.auto_ptr理论上没有引用计数的能力.所以所有依赖于引用计数的特性auto_ptr都不具备.例如:auto_ptr不能处理"野指针"的问题。

3.auto_ptr对象总是认为自己是所包含的资源的唯一拥有者.这导致:

  3.1)一个auto_ptr对象被析构时,如果其持有一个有效的资源,那么这个资源必然被立即delete

  3.2)一个auto_ptr对象被复制(赋值/拷贝构造/参数传递/函数返回)时:

     3.2.1)目标auto_ptr中原有的资源(如果有),必然被立即delete.

     3.2.2)目标auto_ptr获得源auto_ptr原来所持有的资源.

     3.2.3)源auto_ptr失去其所持有的资源,成为一个空闲的对象.

4.auto_ptr作为函数的参数和返回值的时候,要特别注意,形参和实参的结合过程中存在auto_ptr的复制.从而导致资源控制权的转移.从下面的一段代码可以看出问题的严重性:

   1:  //这是一个危险的用法
   2:  template <class T>
   3:  void bad_print(std::auto_ptr<T> p)    //形参P获得了资源的控制权
   4:  {
   5:       //检测p是否持有一个资源
   6:       if (p.get() == NULL) {
   7:               std::cout << "NULL";
   8:        }
   9:       else {
  10:           std::cout << *p;
  11:       }
  12:  }  //函数退出的时候,p的作用域结束.资源也就被释放了.  

5.关于const auto_ptr,const auto_ptr意味着这个auto_ptr对象 永远而且只能 持有这个资源.这意味着:

  5.1)不能将一个const auto_ptr赋值给其它auto_ptr.(这一点和其他对象有很大的不同.而且直接导致auto_ptr不能被放到STL的容器中去).

  5.2)可以使用const auto_ptr&作为参数类型来传递资源,而不会导致资源被意外delete.

  5.3)被持有的资源本身可以被修改.

6.auto_ptr作为成员变量,也可以用这种方法来防止资源泄漏.但是需要注意对auto_ptr的控制权问题.这涉及到

     6.1)拷贝构造函数和赋值操作符重载.是否真的转移控制权还是让两个auto_ptr都控制同一个对象? 多个auto_ptr控制同一个对象极容易导致野指针.

     6.2)是否有函数返回了此auto_ptr.一般应该直接返回此资源,同时要保证此资源没有被其他auto_ptr控制,同时要保证在此auto_ptr所在的对象被销毁后,没有人会再使用此资源.

     6.3)最好是作为const auto_ptr来使用.

7.总结:

    1.尽可能只将auto_ptr对象作为一个函数的局部变量使用;

    2.如果一个大函数重构导致某个auto_ptr需要在多个函数中使用,通过const auto_ptr&方式直接传递过去;

    3.永远不要返回auto_ptr对象,如果一定要这么用,就返回 const auto_ptr&,如果const auto_ptr&不能满足要求,修改你的设计.

    4.不要将auto_ptr放到任何容器中去.当然 我们可以用类似于vector<auto_ptr<Object>*>这么来用.但是很无聊.

    5.将auto_ptr作为成员变量要慎重. 如果有此需要google以下其它带有引用计数的智能指针吧.

 

 

最后,附上 SGI 的 auto_ptr 源码:

template <class _Tp> class auto_ptr {
private:
  _Tp* _M_ptr;

public:
  typedef _Tp element_type;

  explicit auto_ptr(_Tp* __p = 0) __STL_NOTHROW : _M_ptr(__p) {}
  auto_ptr(auto_ptr& __a) __STL_NOTHROW : _M_ptr(__a.release()) {}

#ifdef __STL_MEMBER_TEMPLATES
  template <class _Tp1> auto_ptr(auto_ptr<_Tp1>& __a) __STL_NOTHROW
  : _M_ptr(__a.release()) {}
#endif /* __STL_MEMBER_TEMPLATES */

  auto_ptr& operator=(auto_ptr& __a) __STL_NOTHROW {
  if (&__a != this) {
  delete _M_ptr;
  _M_ptr = __a.release();
  }
  return *this;
  }

#ifdef __STL_MEMBER_TEMPLATES
  template <class _Tp1>
  auto_ptr& operator=(auto_ptr<_Tp1>& __a) __STL_NOTHROW {
  if (__a.get() != this->get()) {
  delete _M_ptr;
  _M_ptr = __a.release();
  }
  return *this;
  }
#endif /* __STL_MEMBER_TEMPLATES */

  // Note: The C++ standard says there is supposed to be an empty throw
  // specification here, but omitting it is standard conforming. Its 
  // presence can be detected only if _Tp::~_Tp() throws, but (17.4.3.6/2)
  // this is prohibited.
  ~auto_ptr() { delete _M_ptr; }

  _Tp& operator*() const __STL_NOTHROW {
  return *_M_ptr;
  }
  _Tp* operator->() const __STL_NOTHROW {
  return _M_ptr;
  }
  _Tp* get() const __STL_NOTHROW {
  return _M_ptr;
  }
  _Tp* release() __STL_NOTHROW {
  _Tp* __tmp = _M_ptr;
  _M_ptr = 0;
  return __tmp;
  }
  void reset(_Tp* __p = 0) __STL_NOTHROW {
  if (__p != _M_ptr) {
  delete _M_ptr;
  _M_ptr = __p;
  }
  }

  // According to the C++ standard, these conversions are required. Most
  // present-day compilers, however, do not enforce that requirement---and, 
  // in fact, most present-day compilers do not support the language 
  // features that these conversions rely on.
  
#if defined(__SGI_STL_USE_AUTO_PTR_CONVERSIONS) && /
  defined(__STL_MEMBER_TEMPLATES)

public:
  auto_ptr(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW
  : _M_ptr(__ref._M_ptr) {}

  auto_ptr& operator=(auto_ptr_ref<_Tp> __ref) __STL_NOTHROW {
  if (__ref._M_ptr != this->get()) {
  delete _M_ptr;
  _M_ptr = __ref._M_ptr;
  }
  return *this;
  }

  template <class _Tp1> operator auto_ptr_ref<_Tp1>() __STL_NOTHROW 
  { return auto_ptr_ref<_Tp1>(this->release()); }
  template <class _Tp1> operator auto_ptr<_Tp1>() __STL_NOTHROW
  { return auto_ptr<_Tp1>(this->release()); }

#endif /* auto ptr conversions && member templates */
};

转载自:http://blog.csdn.net/xkyx_cn/archive/2009/03/05/3960569.aspx

           http://www.cnblogs.com/zhuliangxiong/archive/2009/10/10/1580419.html



你可能感兴趣的:(C++——auto_ptr类)