首页 > 代码库 > Android架构分析之Android智能指针(一)

Android架构分析之Android智能指针(一)

作者:刘昊昱 

博客:http://blog.csdn.net/liuhaoyutz

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<typename T>
 63class sp
 64{
 65public:
 66    inline sp() : m_ptr(0) { }
 67
 68    sp(T* other);
 69    sp(const sp<T>& other);
 70    template<typename U> sp(U* other);
 71    template<typename U> sp(constsp<U>& other);
 72
 73    ~sp();
 74
 75    // Assignment
 76
 77    sp& operator = (T* other);
 78    sp& operator = (const sp<T>&other);
 79
 80    template<typename U> sp& operator= (const sp<U>& other);
 81    template<typename U> 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    template<typename Y>friend class sp;
107    template<typename Y>friend 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<typename T>
121sp<T>::sp(T* other)
122: m_ptr(other)
123  {
124    if (other)other->incStrong(this);
125  }


该构造函数将sp.m_ptr初始化为通过参数传递进来的other。

除了构造函数,对sp对象进行初始化还可能通过赋值运算符,例如:

sp<Object> = new Object

这种情况下,就用到了sp的“=”重载运算符:

162template<typename T>
163sp<T>& sp<T>::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 <class T>
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<const T*>(this);
174        }
175    }
176    //! DEBUGGING ONLY: Getcurrent strong ref count.
177    inline int32_tgetStrongCount() const {
178        return mCount;
179    }
180
181    typedefLightRefBase<T> 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<typename T>
121sp<T>::sp(T* other)
122: m_ptr(other)
123  {
124    if (other)other->incStrong(this);
125  }


sp的重载赋值运算符如下:

162template<typename T>
163sp<T>& sp<T>::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<typename T>
148sp<T>::~sp()
149{
150    if (m_ptr)m_ptr->decStrong(this);
151}


可见,当智能指针sp析构时,会调用m_ptr->decStrong(this)让对应对象的引用计数器减1。