C++11 智能指针——(C++98)auto_ptr

来源:互联网 发布:华为mate9下载软件 编辑:程序博客网 时间:2024/05/17 08:56

    lofter地址:http://willkall.lofter.com/

    C++11从boost库中引入了unique_ptr, shared_ptr, weak_ptr,并舍弃了c98的auto_ptr。

    一、c++98 auto_ptr(参见《C++标准程序库》——孟岩/侯捷译)

    C++标准程序库描述:“auto_ptr是一种智能指针,帮助程序员防止'被异常抛出时发生资源泄露'”。它在对象析构的时候自动释放资源,并模仿了原始指针的操作,重载了operator*和operator->,允许程序员向使用原始指针一样使用auto_ptr(但是没有重载所有的指针算术运算),并减少程序员显示的处理异常发生时代码的复杂度与失误。

    注意:auto_ptr是严格的拥有权类智能指针,使用时注意以下几点

  1. auto_ptr之间不能共享拥有权
  2. auto_ptr对象通过赋值或构造转移拥有权,一旦拥有权转移,此auto_ptr所拥有的将是一个原始指针
  3. auto_ptr不适用于array
  4. auto_ptr不满足STL对容器元素的要求,因此不适用于STL容器。因为在拷贝和赋值之后,新的auto_ptr和旧的auto_ptr对象并不相等。
  5. 如果要阻止拥有权的转移,则应该在停止转移之前,将auto_ptr声明为const
  6. 不要使用auto_ptr的引用作为实参:因为你不知道拥有权到底有没有转移。如果你不需要转移拥有权,请使用const auto_ptr<class> &

  1.auto_ptr的初始化

        auto_ptr不允许使用一般的指针赋值来初始化,必须使用其构造函数来初始化。因为“根据一般指针生成一个auto_ptr”的那个构造函数被声明为explicit(禁止单参构造函数用于自动型别转换)

std::auto_ptr<ClassA> ptr1(new ClassA);//OKstd::auto_ptr<ClassA> ptr2 = new ClassA;//ERROR

   2.auto_ptr拥有权转移

    程序员必须防范一个原始指针被两个auto_ptr所拥有,且在auto_ptr丧失了拥有权后不可以再次使用此auto_ptr

    //initialize an auto_ptr with a new obj    std::auto_ptr<ClassA> ptr1(new ClassA);    //copy the auto_ptr-->transfers the ownership from ptr1 to ptr2    std::auto_ptr<ClassA> ptr2(ptr1);        //ptr1拥有权转移,以后不可再对ptr1使用operator*以及operator->    //like copy    std::auto_ptr<ClassA> ptr3 = ptr2;       //ptr2拥有权转移给ptr3,以后不可再对ptr2使用operator*以及operator->    std::auto_ptr<ClassA> ptr4(new ClassA);  //create a new auto_ptr::ptr4    ptr3 = ptr4;                             //here ptr3 delete the obj it own ,and then ptr4 transfers the ownership to ptr3。ptr4 own a nullptr


    3.auto_ptr之起点和终点

    auto_ptr拥有权的转移特性使得auto_ptr可以方便的实现数据的产生与销毁:在某函数中创建auto_ptr并以值形式返回,或者接受一个以值传递的auto_ptr对象,但不返回auto_ptr。

   

    void sink(auto_ptr<ClassA>);//end the ownership transfer,delete source    auto_ptr<ClassA> generate(void);//generating an auto_ptr
    generate()每次调用都new一个 classA对象,并将对象以及拥有权一起转移个调用端

    sink(auto_ptr<classA>)每次调用有传入一个auto_ptr对象,但是不再返回auto_ptr。因此传入对象的拥有权在sink函数不再转移,并且auto_ptr拥有的资源也得到释放。


    4.使用const auto_ptr

    const auto_ptr意味着你不可以更改这个auto_ptr的拥有权——即你不能转移它也不能对其重新赋值。const auto_ptr比较类似于常指针(T *cosnt p)。

    const auto_ptr<int> ptr(new int);    *ptr = 42;//OK    auto_ptr<int> ptr1(new int);    ptr1 = 58;    ptr = ptr1;//wrong,could not change ptr ownership,COMPLIE-TIME ERROR

   5.auto_ptr源码注解(此处仅列出部分源码,完整源码见auto_ptr.h)
template<typename _Tp>class auto_ptr{private:  _Tp* _M_ptr;public:    typedef _Tp element_type;    explicit auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { }    //禁止原始指针直接赋值时的隐式类型转换    auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { }           //释放拥有权并置其拥有的原始指针为NULL    template<typename _Tp1>    auto_ptr(auto_ptr<_Tp1>& __a) throw() : _M_ptr(__a.release()) { }     //2016-03-05 ????    auto_ptr&operator=(auto_ptr& __a) throw()                             //释放并转移拥有权    {        reset(__a.release());        return *this;    }    template<typename _Tp1>                                                //2016-03-05 为啥要声明一个_TP1类型????    auto_ptr&operator=(auto_ptr<_Tp1>& __a) throw()    {      reset(__a.release());      return *this;    }    ~auto_ptr() { delete _M_ptr; }                                         //析构时释放资源——不适用于数组!!    element_type& operator*() const throw()    {        _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);        return *_M_ptr;    }    element_type* operator->() const throw()    {        _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);        return _M_ptr;   }      element_type* get() const throw() { return _M_ptr; }                  //获取原始指针——获取后切勿delete!!!!     element_type* release() throw()                                       //显示的释放拥有权并置拥有的原始指针为NULL    {        element_type* __tmp = _M_ptr;        _M_ptr = 0;        return __tmp;    }    void reset(element_type* __p = 0) throw()                              //释放拥有权,并获取新的拥有权——可以自赋值    {        if (__p != _M_ptr)        {            delete _M_ptr;            _M_ptr = __p;        }    }};


2016-03-06追加(纯属娱乐):

auto_ptr初始化的时候并不一定需要使用new class来初始化,直接使用一个原始指针即可。但是这样操作很可能出现问题——若用来初始化的原始指针不是一个new出来的存在于堆内存中的对象,则在后续的使用中会造成非法delete(对非new分配的指针调用delete)。以下为一个代码示例:

#include <iostream>#include <memory>using namespace std;void stack_test(int *ip){    auto_ptr<int> aip(ip);}int main(){    int *ip = new int(15);    stack_test(ip);    int c = 49;    int *ip1 = &c;    stack_test(ip1);    cout << "*api = " << *ip << endl;    cout << "*api2 = " << *ip1 << endl;    return 0;}

以下为运行结果:
 

程序可以正常运行(运气不错),但*api得的值已经被覆盖,该指针已经无效。

说明:

1.不管是堆上的还是栈上的指针,都可以用来初始化auto_ptr

2.使用方法1可以使一个原始指针被多个auto_ptr所拥有(这样做非常危险)

3.栈上的内容并没有被修改——是否是因为没有调用delete或者是其他原因?因为c是栈上的元素,无法调用delete删除,在其生命周期中也不会被非法覆盖,所以*api2打印结果正常。但不知道auto_ptr对传入的栈上的指针是如何处理的?

4.另外此处还可说明:auto_ptr析构只是delete并置其拥有的原始指针为nullptr,但是用来初始化它的原始指针值(也就是其指向的地址)是不变的。

5.对上述代码尝试进行try...catch操作,但是没有捕获到异常(是否感到很不正常?)


0 0