聊聊auto_ptr

虽然在C++11中,auto_ptr已经不推荐使用了,代之以功能类似的unique_ptr。但作为曾经的一个C++重要存在,还是来简单了解一下吧。

一、auto_ptr简介

auto_ptr是C++的一个类,它的设计初衷是,动态分配对象以及当对象不再需要时能够自动进行内存回收,为动态分配的对象提供异常安全。auto_ptr类定义在memory头文件中,使用时需要#include <memory>。

智能指针的用法如下:

auto_ptr<T> ap;  // 创建名为ap的未绑定的auto_ptr对象

auto_ptr<T> ap(p);  //创建名为ap的auto_ptr对象,ap拥有指针p指向的对象,该构造函数为explicit

auto_ptr<T> ap1(ap2);  //创建名为ap1的auto_ptr对象,ap1保存原来存储在ap2中的指针,ap2将对象所有权转让给ap1,ap2成为未绑定的auto_ptr对象

ap1 = ap2;  //ap2将所有权转让给ap1,删除ap1指向的对象,并使ap1指向原来ap2指向的对象,使ap2成为未绑定的auto_ptr对象

~ap();   //析构函数,删除ap指向的对象

*ap   //返回ap所绑定的对象的引用

ap->    //返回ap保存的指针

ap.reset(p);   // 如果p与ap的值不同,则删除ap所指向的对象,并将ap绑定到p指向的对象

ap.release();  // 返回ap所保存的指针并且使ap成为未绑定的auto_ptr对象

ap.get();  // 返回ap所保存的指针

智能指针有几点需要说明:

1. 为异常安全的内存分配使用auto_ptr

如果通过常规指针进行内存分配,那么如果在执行delete操作之前发生异常,就不会自动释放内存。看下面一个例子:

void func()
{
    int *p = new int (100);
    // Do something
    delete p;
}

如果在分配内存和delete p之间发生异常,并且该异常不会在函数中被捕获,就不会执行delete p,从而导致内存无法回收。而如果使用auto_ptr,则会执行自动内存释放。

void func()
{
    auto_ptr<int> p(new int (100));
    //Do something
}

2. auto_ptr是可以保存任何类型指针的模板

可以创建任何类型的auto_ptr,类型参数通过auto_ptr后面的尖括号指定,例如:

 auto_ptr<string> sp(new string ("Hello world!"));

3. 将auto_ptr绑定到指针

最常见的情况是,将auto_ptr对象初始化为由new表达式返回的对象的地址,见上面的例子。接受指针的构造函数为explicit构造函数,因此必须使用初始化的直接形式来创建auto_ptr对象。

auto_ptr<int> p1 = new int (1024);   //编译错误,构造函数是explicit,而不能通过implicit使用

auto_ptr<int> p1(new int (1024));  //编译OK

p1所指的由new表达式创建的对象,在超出作用域时自动删除。如果p1是局部对象,p1所指向的对象在定义p1的程序块的末尾删除;如果发生异常,则p1也超出作用域,p1的析构函数将作为异常处理的一部分而自动执行;如果p1是全局对象,就在程序的末尾删除p1引用的对象。

4. auto_ptr的复制和赋值是破坏性操作

auto_ptr和普通指针对待复制和赋值有非常关键的重要区别。以复制为例。对于普通指针来说,在复制之后,两个指针指向同一个对象;而复制auto_ptr对象之后,原来的auto_ptr将基础对象的所有权转让给新的auto_ptr对象,而原来的auto_ptr不再绑定任何基础对象。

auto_ptr<string> sp1(new string ("This is a string."));  //sp1对字符串拥有所有权

auto_ptr<string> sp2(sp1);  //字符串的所有权由sp1转移到sp2,sp1不再指向原字符串,也不指向任何字符串对象

与我们所认知的复制或赋值操作不同,auto_ptr的复制和赋值会改变右操作数,因此,赋值的左右操作数都必须是可修改的左值。

auto_ptr<string> sp3(new string ("Happy birthday!"));

sp3 = sp2;

以上操作中,sp3原本指向字符串“Happy birthday!”;使用sp2对sp3赋值后,删除了sp3原来指向的字符串对象“Happy birthday!”;然后将sp3指向原来sp2指向的字符串对象“This is a string.”;sp2则变成未绑定的auto_ptr。

5. 测试auto_ptr对象使用get()成员

对于普通指针来说,我们可以通过if(p)来判断指针是否为空,而auto_ptr则不能这样直接测试,要测试auto_ptr对象,必须使用它的get成员,该成员返回包含在auto_ptr对象中的基础指针:

//假定p_auto是auto_ptr<int>类型
if(p_auto.get())
{
    *p_auto = 1000;
}

6. reset操作

auto_ptr与普通指针的另一个区别是,不能直接将一个地址赋值给auto_ptr对象,例如下面的操作是错误的:

p_auto = new int (1000);

必须调用reset()来改变指针:

if(p_auto.get())
{
    *p_auto = 1000;
}
else
{
    p_auto.reset(new int (1000));
}

7. auto_ptr对象的使用

auto_ptr类重载了操作符“*”和“->”,因此可以使用类似普通指针的方式使用智能指针。例如:

*sp = "abcdefg";

string str = *sp;

二、auto_ptr的缺陷

auto_ptr类模板为处理动态内存分配和回收提供了安全性和便利性,但它同时也是把双刃剑,一不小心就会跌入陷阱。auto_ptr的使用有如下限制,这也是为什么C++11不再推荐使用的原因,因为确实有点反常识。

1. 不能使用auto_ptr对象保存指向静态分配对象的指针,否则,当auto_ptr对象本身被销毁的时候,它会试图去释放非动态分配的内存,导致未定义行为;

2. 不能使用两个auto_ptr指向同一对象,因为在两个auto_ptr对象被销毁时,会导致同一个对象被释放两次;

3. auto_ptr只能用于管理从new返回的一个对象,而不能管理动态分配的数组,因为其释放对象时使用的是delete操作符,而不是用释放数组的delete[]操作符。

4. 不能将auto_ptr对象存储在STL标准容器中,因为容器要求所保存的类型定义复制和赋值两种操作,在复制和赋值之后,两个对象必须具有相同的值,而auto_ptr不满足这个要求。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章