auto_ptr 类

来源:互联网 发布:杰视帮和凤凰美工 编辑:程序博客网 时间:2024/06/11 12:56

 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;
ap1 = ap2; // 释放内存
正是因为拷贝和赋值会破坏右操作数,所以 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 管理的指针将被析构两次,这必然带来严重的后果。
3)reset 函数,重设需要管理的指针,首先 auto_ptr 会删除当前管理的对象,然后再设置新的对象的指针。另外:
pNum = pNum; // 不会发送任何事情,安全
pNum.reset(pNum->get()); // 不会发送任何事情,安全
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。 

最后,附上 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 */
};