auto_ptr模板类
auto_ptr模板定义了类似指针的对象,可以将new获得(直接或间接)的地址赋给这种对象。当auto_ptr对象过期时,其析构函数将使用delete来释放内存。
要创建auto_ptr对象,必须包含头文件memory。使用通常的模板句法来实例化所需类型的指针。模板中包括:
template<class X>
class auto_ptr
{ public:
explicit auto_ptr(X * p = 0) throw();
... };
throw()意味着构造函数不引发异常。因此,请求X类型的auto_ptr将获得一个指向X类型的auto_ptr:
auto_ptr<double> pd(new double); // an auto_ptr to double
// (use in place of double *)
auto_ptr<string> ps(new string); // an auto_ptr to string
// (use in place of string *)
上述中new double是new返回的指针,指向新分配的内存块。它是auto_ptr<double>构造函数的参数,即它是对应于原型中形参 X* p 的实参。
范例:
#include <memory>
void remodel (string & str)
{ auto_ptr<string> ps(new string(str));
...
if (weird_thing())
throw exception();
str = *ps;
//delete ps: NO LONGER NEEDED
return;
}
auto_ptr的构造函数是显示的,因此不存在从指针到auto_ptr对象的隐式类型转换:
auto_ptr<double> pd;
double *p_reg = new double;
pd = p_reg; // not allowed(implicit conversion)
pd = auto_ptr<double> (p_reg); // allowed(explicit conversion)
auto_ptr<double> pauto = p_reg; // not allowed(implicit conversion)
auto_ptr<double> pauto (p_reg); // allowed(explicit conversion)
auto_ptr是一种智能指针(smart pointer)——类似于指针,但特性比指针更多。
auto_ptr类被定义为在很多方面与常规指针类似。例如,如果ps是一个auto_ptr,则可以对它执行解除引用操作(*ps)和递增操作(++ps),用它来访问结构成员(ps->puffIndex),将它赋给指向相同类型的常规指针。还可以将auto_ptr赋给另一个同类型的auto_ptr,但将引发一个问题,请看下面“auto_ptr赋值问题”。
auto_ptr的注意事项
由于auto_ptr模板使用的是delete,因此它只能与new一起使用:
auto_ptr<int> pi (new int[200]); //NO
这将引发错误。
可以复制头文件memory中的auto_ptr模板,然后对其进行修改,使之使用delete[]。
下述代码将delete操作符用于非堆内存,而引发错误:
string vacation("I wandered lonely as a cloud.");
auto_ptr<string> pvac (&vacation); //NO
警告: 只能对new分配的内存使用auto_ptr对象,而不要对由new[]分配的或者通过声明变量分配的内存使用它。
auto_ptr赋值问题
auto_ptr<string> ps (new string("I reigned lonely as a cloud.") );
auto_ptr<string> vocation;
vocation = ps;
两个指针将指向同一个string对象,其中一个是另一个的拷贝。当ps和vocation都过期时,程序将试图删除同一个对象两次。
解决办法:
(1) 定义赋值操作符,使之执行深度复制。这样两个指针将指向不同的对象,其中的一个对象是另一个对象的拷贝。
(2) 建立所有权(ownership)概念,对于特定的对象,只能有一个智能指针可拥有它。智能指针的构造函数只能删除该智能指针拥有的对象。并使赋值操作转让所有权。这就是用于auto_ptr的策略。
(3) 创建智能更高的指针,跟踪引用特定对象的智能指针数。这被称为引用计数(reference counting)。例如,赋值时,计数器加1;指针过期时,计数将减1。仅当最后一个指针过期时,delete才被调用。
(同样的策略也适用于复制构造函数)