智能指針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), 接收全部權,接收以前擁有其它指針的話,必須先釋放其空間。

相關文章
相關標籤/搜索