zoukankan      html  css  js  c++  java
  • 智能指针auto_ptr详解

    主要内容转自http://www.cppblog.com/SmartPtr/archive/2007/07/05/27549.html

    1. 智能指针auto_ptr的引入

    auto_ptr是C++标准库中的智能指针模板类,头文件<memory>

    auto_ptr的出现,主要是为了解决“有异常抛出时发生内存泄漏”的问题。如下的简单代码是这类问题的一个简单示例。

    int* p = new int(100);
    try
    {
        doSomething();
    
        cout << *p << endl;
    
        delete p;
    }
    catch(exception& e)
    {
        
    }

    当doSomething();部分抛出异常,将导致指针p所指向的空间得不到释放而导致内存泄露。auto_ptr的引入解决了这类问题。

    2. auto_ptr的源代码(未可读性进行了少许改动的源码)

     1 namespace std
     2 {
     3  template<class T>
     4  class auto_ptr 
     5  {
     6  private:
     7   T* ap; 
     8  public:
     9 
    10   // constructor & destructor ----------------------------------- (1)
    11   explicit auto_ptr (T* ptr = 0) throw() : ap(ptr){}
    12 
    13   ~auto_ptr() throw() 
    14   {
    15    delete ap;
    16   }
    17 
    18   
    19   // Copy & assignment --------------------------------------------(2)
    20   auto_ptr (auto_ptr& rhs) throw() :ap(rhs.release()) {}
    21   template<class Y>  
    22   auto_ptr (auto_ptr<Y>& rhs) throw() : ap(rhs.release()) { }
    23 
    24   auto_ptr& operator= (auto_ptr& rhs) throw() 
    25   {
    26    reset(rhs.release());
    27    return *this;
    28   }
    29   template<class Y>
    30   auto_ptr& operator= (auto_ptr<Y>& rhs) throw() 
    31   {
    32    reset(rhs.release());
    33    return *this;
    34   }
    35 
    36   // Dereference----------------------------------------------------(3)
    37   T& operator*() const throw() 
    38   {
    39    return *ap;
    40   }
    41   T* operator->() const throw() 
    42   {
    43    return ap;
    44   }
    45 
    46   // Helper functions------------------------------------------------(4)
    47   // value access
    48   T* get() const throw() 
    49   {
    50    return ap;
    51   }
    52 
    53   // release ownership
    54   T* release() throw()
    55   {
    56    T* tmp(ap);
    57    ap = 0;
    58    return tmp;
    59   }
    60 
    61   // reset value
    62   void reset (T* ptr=0) throw() 
    63   {
    64    if (ap != ptr) 
    65    {
    66     delete ap;
    67     ap = ptr;
    68    }
    69   }
    70 
    71   // Special conversions-----------------------------------------------(5)
    72   template<class Y>
    73   struct auto_ptr_ref
    74   {
    75    Y* yp;
    76    auto_ptr_ref (Y* rhs) : yp(rhs) {}
    77   };
    78 
    79   auto_ptr(auto_ptr_ref<T> rhs) throw() : ap(rhs.yp) { }
    80   auto_ptr& operator= (auto_ptr_ref<T> rhs) throw() 
    81   {  
    82    reset(rhs.yp);
    83    return *this;
    84   }
    85   template<class Y>
    86   operator auto_ptr_ref<Y>() throw() 
    87   {
    88    return auto_ptr_ref<Y>(release());
    89   }
    90   template<class Y>
    91   operator auto_ptr<Y>() throw()
    92   {
    93    return auto_ptr<Y>(release());
    94   }
    95  };
    96 }

    3. auto_ptr的使用

    3.1 创建auto_ptr对象

    auto_ptr构造时取得某个对象的所有权,在析构时释放该对象。我们实际上是创建一个auto_ptr<Type>类型的局部对象,该局部对象析构时,会将自身所拥有的指针空间释放,所以不会有内存泄露。

    auto_ptr<int> p(new int(1));//推荐
    
    //
    int* np = new int(1);
    auto_ptr<int> p(np);

    创建auto_ptr对象时注意的几个问题

    (1) auto_ptr的构造函数为explicit,阻止了一般指针隐式类型转换为auto_ptr的构造,所以如下的创建方式是编译不过的。

    int* p = new int(1);
    auto_ptr<int> ap = p;

    如下代码详细解释了关于explicit的作用。

    #include <iostream>
    using namespace std;
    
    class Test1
    {
            public:
                    Test1(int i):iValue(i){};
            private:
                    int iValue;
                    char cValue;
    };
    
    class Test2
    {
            public:
                    explicit Test2(int i):iValue(i){};
            private:
                    int iValue;
                    char cValue;
    };
    
    int main(int argc, char* argv[])
    {
            Test1 t1 = 1;//t1.iValue值为1,cValue值为char类型默认值
            Test2 t2 = 2;//编译不过, error: conversion from 'int' to non-scalar type 'Test2' requested
    }

    (2) 由于auto_ptr对象析构时会删除它所拥有的指针,所以使用时避免多个auto_ptr对象管理同一个指针。如下的使用方法应该避免。

    int* np = new int(1);
    auto_ptr<int> p1(np);
    auto_ptr<int> p2(np);

    这样使用会造成p1和p2在析构时都试图删除np,C++标准中多次删除同一个对象会导致未定义的行为。且当p1析构而p2仍然被使用时,会导致空指针访问风险。

    (3)auto_ptr的内部实现中,析构函数中删除对象使用delete而不是delete[],所以auto_ptr不能用来管理数组指针。

    int *p = new int[100];
    auto_ptr<int> ap(p);

    如上使用auto_ptr的方式,在ap析构时,执行delete,仅仅释放了数组的第一个元素的空间,仍然会造成内存泄漏,所有使用auto_ptr管理数组不合理的。

    (4)C++中对一个空指针NULL执行delete操作是安全的。所以在auto_ptr的析构函数中无须判断它所拥有指针是否为空。

    3.2 auto_ptr的拷贝构造和赋值

    auto_ptr要求对它所拥有的指针完全占有,这一点与引用计数的智能指针不同,也就是说,一个一般指针不能同时被两个auto_ptr所拥有,一方面使用者要避免将用同一个指针构造auto_ptr(3.1(2)的那种方式),另一方面auto_ptr在拷贝构造和赋值运算符重载时要做特殊处理,具体的做法是对所有权进行了完全转移,在拷贝和赋值时,剥夺原auto_ptr对指针的拥有权,赋予当前auto_ptr对指针的拥有权,当前auto_ptr获得auto_ptr的指针,并使原auto_ptr的指针置空,由于会修改原对象,所以auto_ptr的拷贝构造函数以及赋值运算符重重载函数的参数是引用而不是常(const)引用。

    这部分需要注意的几个问题

    (1) auto_ptr对象被拷贝或者被赋值后,已经失去了对原指针的所有权,此时,对这个auto_ptr的读取操作是不安全的。如下代码是不安全的。

    auto_ptr<int> p1(new int(1));
    auto_ptr<int> p2(p1);
    cout << *p1 << endl;
    
    //and
    auto_ptr<int> p3=p1;
    cout << *p1 << endl;

    这种情况较为隐蔽的情形出现在将auto_ptr作为函数参数按值传递,因为在函数调用过程中在函数的作用域中会产生一个局部的临时auto_ptr对象来接收传入的 auto_ptr(拷贝构造),这样,传入的实参auto_ptr的对其指针的所有权转移到了临时auto_ptr对象上,临时auto_ptr在函数退出时析构,所以当函数调用结束,原实参所指向的对象已经被删除了。

    void func(auto_ptr<int> ap)
    {
    cout << *ap << endl;
    }
    
    auto_ptr<int> ap(new int(1));
    func(ap);
    cout << *ap1 << endl;//错误,函数调用结束后,ap1已经不再拥有任何对象了

    因此要避免使用auto_ptr对象作为函数参数按值传递,按引用传递在调用函数是不会发生所有权转移,但是无法预测函数体内的操作,有可能在函数体内进行了所有权的转移,因此按引用传递auto_ptr作为函数参数也是不安全的。使用const 引用传递则可以阻止在函数体内对auto_ptr对象的所有权转移。如果不得不使用auto_ptr对象作为函数参数时,尽量使用const引用传递参数。

    (2) auto_ptr支持所拥有的指针类型之间的隐式类型转换。

    class base{};
    class derived: public base{};
    //下列代码就可以通过,实现从auto_ptr<derived>到auto_ptr<base>的隐式转换,因为derived*可以转换成base*类型
    auto_ptr<base> apbase = auto_ptr<derived>(new derived);

    (3) C++的STL容器对于容器元素类型的要求是有值语义,即可以赋值和复制。auto_ptr在赋值和复制时都进行了特殊操作,所以auto_ptr对象不能作为STL容器元素。

    3.3 auto_ptr对象的提领操作。

    可以像使用一般指针一样,通过*和->运算符对auto_ptr所有用的指针进行提领操作。首先必须确保这个auto_ptr对象确实拥有某个指针,否则,这个操作的行为即对空指针的提领是未定义的。

    struct A
    {
     void f();
    }
    auto_ptr<A> apa(new A);
    (*apa).f();
    apa->f();

    3.4 auto_ptr的辅助函数

    (1) T* get(),获得auto_ptr所拥有的指针。

    (2) T* release(), 释放auto_ptr的所有权,并将所有用指针返回。

    (3) void reset(T* ptr=0), 接收所有权,接收之前拥有其它指针的话,必须先释放其空间。

  • 相关阅读:
    实现第三方系统单点登录
    python爬虫
    webot设备motor的api
    webots学习
    python学习算术运算
    python快捷键与命令函数
    python学习构造和析构
    python学习对象相关的bif
    python学习对象:拾遗
    matlab基础知识
  • 原文地址:https://www.cnblogs.com/litterrondo/p/3187342.html
Copyright © 2011-2022 走看看