Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android架構分析之Android智能指針(一)

Android架構分析之Android智能指針(一)

編輯:關於Android編程

 

Android版本:4.4.2

 

在C++語言中,指針操作是最容易問題的地方,常見的指針操作錯誤有以下幾種:

1、 定義一個指針,但沒有對其進行初始化。這種情況下,指針會指向一個隨機地址,此時使用該指針,將出現不可預知的錯誤。一般定義一個指針時,應該同時對該指針進行初始化。

2、 new了一個對象後,忘記delete該對象。這種情況會造成內存洩漏,時間久了,重復多次,可能造成系統宕機。

3、 野指針。例如,我們new了一個對象A,並用指針p1指向A,使用結束後,我們delete了對象A,此時,p1還是指向A原來的地址,但是A被delete後,該地址是非法地址。這樣的p1就是野指針。再舉一個例子,p1和p2兩個指針都指向A,我們通過p1指針delete了A之後,將p1設置為NULL,但p2仍然指向A原來地址,此時,p2就是野指針。

為了避免上述C++指針使用錯誤,Android為我們提供了智能指針,定義在frameworks/rs/cpp/util目錄下的RefBase.h和StrongPointer.h文件中。

Android智能指針是一個模板類,又分為強指針sp和弱指針wp。強指針sp定義如下:

 

 62template
 63class sp
 64{
 65public:
 66    inline sp() : m_ptr(0) { }
 67
 68    sp(T* other);
 69    sp(const sp& other);
 70    template sp(U* other);
 71    template sp(constsp& other);
 72
 73    ~sp();
 74
 75    // Assignment
 76
 77    sp& operator = (T* other);
 78    sp& operator = (const sp&other);
 79
 80    template sp& operator= (const sp& other);
 81    template sp& operator= (U* other);
 82
 83    //! Special optimization for use byProcessState (and nobody else).
 84    void force_set(T* other);
 85
 86    // Reset
 87
 88    void clear();
 89
 90    // Accessors
 91
 92    inline T&      operator* ()const  { return *m_ptr; }
 93    inline T*      operator-> () const {return m_ptr;  }
 94    inline T*      get() const         { return m_ptr; }
 95
 96    // Operators
 97
 98    COMPARE(==)
 99    COMPARE(!=)
100    COMPARE(>)
101    COMPARE(<)
102    COMPARE(<=)
103    COMPARE(>=)
104
105private:
106    templatefriend class sp;
107    templatefriend class wp;
108    void set_pointer(T* ptr);
109    T* m_ptr;
110};


 

66-71行,定義了5種sp構造函數。

73行,定義了sp的析構函數。

77-81行,定義了4種“=”運算符的重載函數。

92-103行,對其它8種運算符進行重載。每個COMPARE宏對應該運算符的6個重載函數。

109行,定義T類型指針變量m_ptr。這個指針變量m_prt即是sp類的核心。

我們可以這樣理解sp類:

1、sp類的對象實例用來替代我們原來所用的指針。

2、sp類是對指針的封裝。sp.m_prt即我們原來所用的指針。

3、通過使用sp類的對象代替指針,可以避免出現原來使用指針時常見的錯誤。

為什麼說使用sp類的對象代替指針,就可以避免原來使用指針時常見的錯誤呢?

首先來看使用指針的第一種常見錯誤,即定義指針時沒有進行初始化。

使用sp類對象代替指針後,創建sp類對象時會調用到sp類構造函數,在構造函數中,會對sp.m_ptr進行初始化,例如:

 

66    inline sp() : m_ptr(0) { }


 

默認構造函數將sp.m_ptr初始化為0。

再比如:

 

120template
121sp::sp(T* other)
122: m_ptr(other)
123  {
124    if (other)other->incStrong(this);
125  }


 

該構造函數將sp.m_ptr初始化為通過參數傳遞進來的other。

除了構造函數,對sp對象進行初始化還可能通過賦值運算符,例如:

sp

這種情況下,就用到了sp的“=”重載運算符:

 

162template
163sp& sp::operator = (T* other)
164{
165    if (other) other->incStrong(this);
166    if (m_ptr)m_ptr->decStrong(this);
167    m_ptr = other;
168    return *this;
169}


 

可以看到,在“=”重載運算符中,167行,將參數傳遞進來的other賦值給sp.m_ptr。

這樣通過在構造函數和重載賦值運算符中完成對sp.m_ptr的初始化,即避免了使用指針的第一種常見錯誤(定義指針時忘記初始化)。

 

使用指針的第二種常見錯誤(new一個對象後忘記delete)和第三種常見錯誤(野指針)可以通過給被指針指向的對象加一個引用計數器來解決。我們可以想象一下,如果被指針指向的對象有一個引用計數器,即當有一個指針指向該對象時,該對象引用計數器為1,有兩個指針指向該對象時,該對象引用計數器為2,依次類推。反之,當一個指針不再指向該對象時,該對象引用計數器的值減1,當對象引用計數器的值為0時,該對象需要被delete。

怎樣給對象設置一個引用計數器呢?Android智能指針的做法是讓該對象對應的類繼承LightRefBase模板類,該類定義在frameworks/rs/cpp/util/RefBase.h文件中:

 

163template 
164class LightRefBase
165{
166public:
167    inline LightRefBase() :mCount(0) { }
168    inline voidincStrong(__attribute__((unused)) const void* id) const {
169       __sync_fetch_and_add(&mCount, 1);
170    }
171    inline voiddecStrong(__attribute__((unused)) const void* id) const {
172        if(__sync_fetch_and_sub(&mCount, 1) == 1) {
173            deletestatic_cast(this);
174        }
175    }
176    //! DEBUGGING ONLY: Getcurrent strong ref count.
177    inline int32_tgetStrongCount() const {
178        return mCount;
179    }
180
181    typedefLightRefBase basetype;
182
183protected:
184    inline ~LightRefBase() { }
185
186private:
187    friend classReferenceMover;
188    inline static void moveReferences(void*,void const*, size_t,
189            constReferenceConverterBase&) { }
190
191private:
192    mutable volatile int32_tmCount;
193};


 

192行,定義了一個整數mCount,這就是所謂的引用計數器。

167行,LightRefBase的構造函數將引用計數器mCount初始化為0。

168-170行,定義了incStrong函數,用於將引用計數器mCount的值加1。

171-175行,定義了decStrong函數,用於將引用計數器mCount的值減1,需要注意的是,如果減1之前,mCount的值為1,說明對本對象最後的引用也解除了,則會delete本對象,這樣就避免了我們所說的new一個對象,忘記delete。

 

知道了LightRefBase的定義,我們再回過頭來看sp類,就能理解智能指針是怎樣工作的了。

sp的構造函數如下:

 

120template
121sp::sp(T* other)
122: m_ptr(other)
123  {
124    if (other)other->incStrong(this);
125  }


 

sp的重載賦值運算符如下:

 

162template
163sp& sp::operator = (T* other)
164{
165    if (other)other->incStrong(this);
166    if (m_ptr)m_ptr->decStrong(this);
167    m_ptr = other;
168    return *this;
169}


 

類T繼承LightRefBase,可以看到,通過構造函數或賦值運算讓sp對象指向T對象,除了將other賦值給sp.m_ptr外,因為這兩種情況都屬於增加一個對象引用計數,所以還會調用other->incStrong(this)。特別需要注意的是,在賦值運算中,如果m_ptr之前指向其它值,則需要先調用m_ptr->decStrong(this),即對應對象引用計數減1,然後再將other賦值給sp.mptr。

sp的析構函數如下:

 

147template
148sp::~sp()
149{
150    if (m_ptr)m_ptr->decStrong(this);
151}


 

可見,當智能指針sp析構時,會調用m_ptr->decStrong(this)讓對應對象的引用計數器減1。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved