智能指针auto_ptr详解

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

1. 智能指针auto_ptr的引入ios

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的源代码(未可读性进行了少量改动的源码)this

 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的使用spa

3.1 建立auto_ptr对象scala

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

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

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

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

(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), 接收全部权,接收以前拥有其它指针的话,必须先释放其空间。

相关文章
相关标签/搜索