作者:刘昊昱

博客: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。

更多相关文章

  1. 初学Android,使用Drawable资源之使用StateListDrawable资源(十三
  2. 安卓 每日一题 2020年5月、6月
  3. AIDL基本用法
  4. Google Android(安卓)Developer
  5. android中的内存优化
  6. Qt for Android(安卓)- JNI
  7. Android(安卓)实现微信分享好友和朋友圈
  8. android 定制 View派生类
  9. Android消息队列模型——Thread,Handler,Looper,Massage Queue

随机推荐

  1. Android(安卓)2.3 API改变大全
  2. android 按钮点击效果 imageview button
  3. Android中Java和JavaScript交互
  4. android资源类型
  5. Android开发之动态库调用
  6. Android(安卓)IntentService使用
  7. Android(安卓)内存溢出问题分析
  8. Android(安卓)Toolchain与Bionic Libc
  9. Android(安卓)如何自定义apk打包的包名
  10. ListView分隔线