auto_ptr是当前C++标准库中提供的一种智能指针,或许相对于boost库提供的一系列眼花缭乱的智能指针, 或许相对于Loki中那个无所不包的智能指针,这个不怎么智能的智能指针难免会黯然失色。诚然,auto_ptr有这样那样的不如人意,以至于程序员必须像使用”裸“指针那样非常小心的使用它才能保证不出错,以至于它甚至无法适用于同是标准库中的那么多的容器和一些算法,但即使如此,我们仍然不能否认这个小小的auto_ptr所蕴含的价值与理念。
auto_ptr的出现,主要是为了解决“被异常抛出时发生资源泄漏”的问题。即如果我们让资源在局部对象构造时分配,在局部对象析构时释放。这样即使在函数执行过程时发生异常退出,也会因为异常能保证局部对象被析构从而保证资源被释放。auto_ptr就是基于这个理念而设计, 这最早出现在C++之父Bjarne Stroustrup的两本巨著TC++PL和D&E中,其主题为"resource acquisition is initialization"(raii,资源获取即初始化),然后又在Scott Meyer的<<More Effective C++>>中相关章节的推动下,被加入了C++标准库。
下面我就列出auto_ptr的源代码,并详细讲解每一部分。因为标准库中的代码要考虑不同编译器支持标准的不同而插入了不少预编译判断,而且命名可读性不是很强(即使是侯捷老师推荐的SGI版本的stl,可读性也不尽如人意), 这里我用了Nicolai M. Josuttis(<<The C++ standard library>>作者)写的一个auto_ptr的版本,并做了少许格式上的修改以易于分析阅读。
{
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());
}
};
}
1 构造函数与析构函数
auto_ptr在构造时获取对某个对象的所有权(ownership),在析构时释放该对象。我们可以这样使用auto_ptr来提高代码安全性:
int* p = new int(0);
auto_ptr<int> ap(p);
从此我们不必关心应该何时释放p, 也不用担心发生异常会有内存泄漏。
这里我们有几点要注意:
1) 因为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.
int* pa = new int[10];
auto_ptr<int> ap(pa);
因为auto_ptr的析构函数中删除指针用的是delete,而不是delete [],所以我们不应该用auto_ptr来管理一个数组指针。
3) 构造函数的explicit关键词有效阻止从一个“裸”指针隐式转换成auto_ptr类型。
4) 因为C++保证删除一个空指针是安全的, 所以我们没有必要把析构函数写成:
~auto_ptr() throw()
{
if(ap) delete ap;
}