当前位置: 代码迷 >> Android >> Android架构分析之Android智能指针(1)
  详细解决方案

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

热度:39   发布时间:2016-04-28 05:28:06.0
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(>=)104105private: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 LightRefBase165{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    }180181    typedefLightRefBase<T> basetype;182183protected:184    inline ~LightRefBase() { }185186private:187    friend classReferenceMover;188    inline static void moveReferences(void*,void const*, size_t,189            constReferenceConverterBase&) { }190191private: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。

  相关解决方案