智能指针


Android系统的应用程序框架层中, 有一部分代码是使用C++语言开发 .

C++最容易出错的地方就是指针, 一般为忘记释放指针指向的对象所占用的内存, 或者使用了无效指针。

所以, Android系统就为我们提供了C++智能指针, 可以避免出现指针使用不当的问题

为了解决以上问题 , 通常通过引用计数技术来维护对象的生命周期

每当有一个新的指针指向了一个对象时, 这个对象的引用计数增加1 ; 每当有一个指针不再指向一个对象时, 这个对象的引用计数就减少1 ; 当对象的引用计数为0时, 它所占用的内存就可以安全地释放了 .

智能指针正是一种能够自动维护对象引用计数的技术。 这里需要特别强调的是, 智能指针是一个对象, 而不是一个指针, 但是它引用了一个实际使用的对象。

在智能指针构造时, 增加它所引用的对象的引用计数; 而在智能指针析构时, 就减少它所引用的对象的引用计数 .

当 有两个对象A和B, 对象A引用了对象B, 而对象B也引用了对象A。 一方面, 当对象A不再使用时, 就可以释放它所占用的内存了, 但是由于对象B仍然在引用着它, 因此, 此时对象A就不能被释放; 另一方面, 当对象B不再使用时, 就可以释放它所占用的内存了, 但是由于对象A仍然在引用着它, 因此, 此时对象B也不能被释放

这个问题也是 垃圾收集(Garbage Collection)系统所遇到的经典问题之一, 因为它一次只能收集一个对象所占用的内存。

为了解决以上问题 , 采取另外一种稍为复杂的引用计数技术来维护对象的生命周期了。 这种引用计数技术将对象的引用计数分为强引用计数弱引用计数两种, 其中, 对象的生命周期只受强引用计数控制

在“父-子”关系中, “父”对象通过强引用计数来引用“子”对象; “子”对象通过弱引用计数来引用“父”对象。

假设对象A为 父 , 对象B为子。 对象A通过强引用计数来引用对象B, 而对象B通过弱智能指针引用计数来引用对象A。 当对象A不再使用时, 对象A的生命周期不受对象B的影响, 此时对象A可以安全地释放。 在释放对象A时, 同时也会释放它对对象B的强引用计数, 因此, 当对象B不再使用时, 对象B也可以安全地释放了。

对象的生命周期不受弱引用计数控制, 当对象B想要使用对象A时,先要把对 对象A的弱引用计数升级为强引用计数, 然后才能使用它; 如果对象B不能将对象A的弱引用计数升级为强引用计数, 就说明对象A已经被释放了, 那 对象B就不能再使用它。

Android系统提供了三种类型的C++智能指针, 分别为轻量级指针(Light Pointer)、 强指针(StrongPointer)和弱指针(Weak Pointer), 其中, 轻量级指针使用了简单的引用计数技术, 而强指针和弱指针使用了强引用计数和弱引用计数技术

Android系统将引用计数器定义为一个公共类, 所有支持使用智能指针的对象类都必须要从这个公共类继承下来。 这样, Android系统的智能指针就可以通过这个引用计数器来维护对象的生命周期


轻量级指针

轻量级指针通过简单的引用计数技术来维护对象的生命周期。如果一个类的对象支持使用轻量级指针,那么它就必须要从LightRefBase类继承下来,因为LightRefBase类提供了一个简单的引用计数器。


实现原理分析

分析LightRefBase类的实现原理

// frameworks\base\include\utils\RefBase.h

// 模板类
// T 表示对象的实际类型,它必须是继承了LightRefBase类的
template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }

    // 增加它所引用的对象的引用计数
    inline void incStrong(const void* id) const 
    {
        android_atomic_inc(&mCount);
    }

    //减少它所引用的对象的引用计数
    inline void decStrong(const void* id) const 
    {
        if (android_atomic_dec(&mCount) == 1) 
        {
            // 对象的引用计数值 == 0
            //释放这个对象所占用的内存
            delete static_cast<const T*>(this);
        }
    }
    //! DEBUGGING ONLY: Get current strong ref count.
    inline int32_t getStrongCount() const 
    {
        return mCount;
    }

protected:
    inline ~LightRefBase() { }

private:
    mutable volatile int32_t mCount;    // 描述一个对象的引用计数值
};

轻量级指针的实现类为sp,它同时也是强指针的实现类 :

// frameworks\base\include\utils\RefBase.h

// 模块类,其中,模板参数T表示对象的实际类型,它也是必须继承了LightRefBase类的
template <typename T>
class sp
{
public:
    // 维护它所引用的对象的强引用计数和弱引用计数
    typedef typename RefBase::weakref_type weakref_type;

    // m_ptr 在构造函数里面初始化的,指向实际引用的对象
    inline sp() : m_ptr(0) { }

    sp(T* other);
    sp(const sp<T>& other);
    template<typename U> sp(U* other);
    template<typename U> sp(const sp<U>& other);

    ~sp();

    // Assignment

    sp& operator = (T* other);
    sp& operator = (const sp<T>& other);

    template<typename U> sp& operator = (const sp<U>& other);
    template<typename U> sp& operator = (U* other);

    //! Special optimization for use by ProcessState (and nobody else).
    void force_set(T* other);

    // Reset

    void clear();

    // Accessors

    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }

    // Operators

    COMPARE(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)

private:    
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;

    // Optimization for wp::promote().
    sp(T* p, weakref_type* refs);

    T*              m_ptr;
};

sp类的构造函数有两个版本,一个是普通的构造函数,一个是拷贝构造函数 :

// frameworks\base\include\utils\RefBase.h

/* 由于成员变量m_ptr所指向的对象是从LightRefBase类继承下来的, 因此,这两个构造函数实际上是调用了LightRefBase类的成员函数incStrong来增加对象的引用计数。 */

// 首先初始化成员变量m_ptr
template<typename T>
sp<T>::sp(T* other)
    : m_ptr(other)
{
    if (other) 
    {
        // 调用它的成员函数incStrong来增加它的引用计数
        other->incStrong(this);
    }
}

// 首先初始化成员变量m_ptr
template<typename T>
sp<T>::sp(const sp<T>& other)
    : m_ptr(other.m_ptr)
{
    if (m_ptr) 
    {
        // 调用它的成员函数incStrong来增加它的引用计数
        m_ptr->incStrong(this);
    }
}

sp类的析构函数的实现 :

// frameworks\base\include\utils\RefBase.h

/* 析构函数与构造函数相反,即调用成员变量m_ptr所指向的对象的成员函数decStrong来减少它的引用计数, 实际上是调用LightRefBase类的成员函数decStrong来减少对象的引用计数 */

template<typename T>
sp<T>::~sp()
{
    if (m_ptr) 
    {
        // 调用成员函数decStrong来减少它的引用计数
        m_ptr->decStrong(this);
    }
}

应用实例分析

external目录中建立一个C++应用程序lightpointer来说明轻量级指针的使用方法,它的目录结构如下:

~/Android
    external
        lightpointer
            lightpointer.cpp # 源文件
            Android.mk # 编译脚本文件

lightpointer.cpp :

#include <stdio.h>
#include <utils/RefBase.h>

using  namespace android;

// 继承了LightRefBase类的LightClass类 , 能够结合轻量级指针来使用
class LightClass : public LightRefBase<LightClass>
{
public:
    LightClass()
    {
        printf("Construct LightClass Object.\n");
    }

    virtual ~LightClass()
    {
        printf("Destory LightClass Object.\n");
    }

};

// lightpointer 的入口函数 main
int main(int argc, char** argv)
{
    // 创建了一个 LightClass 对象 pLightClass
    LightClass* pLightClass = new LightClass();

    // 创建一个轻量级指针 lpOut 来引用它
    // 创建过程中, 调用 sp 类的构造函数来增加 LightClass 对象 pLightClass 的引用计数,
    // 即此时 LightClass 对象 pLightClass 的引用计数值为 1
    sp<LightClass> lpOut = pLightClass;

    // 打印 == 1
    printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());

    //内嵌的作用域
    {
        /* * 创建了另外一个轻量级指针 lpInner 来引用 LightClass 对象 pLightClass * LightClass 对象 pLightClass 的引用计数值会再增加1 */
        sp<LightClass> lpInner = lpOut;

        // printf语句打印出来的数字就应该等于2
        printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());
    }
    /* * 当应用程序 lightpointer 跳出了作用域之后,轻量级指针 lpInner 就被析构了, *这时候 LightClass 对象 pLightClass 的引用计数值就会减少 1 */

    //打印 == 1
    printf("Light Ref Count: %d.\n", pLightClass->getStrongCount());

    return 0;
     /* *当应用程序 lightpointer 执行完成之后,轻量级指针 lpOut 也会被析构, *这时 LightClass 对象 pLightClass 的引用计数值== 0,于是LightClass对象pLightClass就会被释放 */
}

Android.mk

应用程序lightpointer的编译脚本文件

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := lightpointer
LOCAL_SRC_FILES := lightpointer.cpp
# 引用了libcutils 和 libutils 两个库
LOCAL_SHARED_LIBRARIES := \
    libcutils \
    libutils
include $(BUILD_EXECUTABLE)

对这个C++工程进行编译打包

mmm ./external/lightpointer/
make snod

编译成功之后,就可以在 out/target/product/gerneric/system/bin 目录下看到应用程序文件lightpointer了;

打包成功之后,该应用程序就包含在out/target/product/gerneric目录下的Android系统镜像文件system.img中了。

# 使用新得到的系统镜像文件 system.img 来启动 Android 模拟器
emulator &

# 使用adb工具连接上它
adb shell

# 进入到/system/bin 目录中
cd system/bin/

# 运行应用程序 lightpointer 来查看它的输出
./lightpointer

由于个人水平有限, 难免有些错误, 希望各位点评

  • @Author: cpu_code
  • @Date: 2020-07-12 14:09:20
  • @LastEditTime: 2020-07-17 19:13:54
  • @FilePath: \notes\android_bottom\smart_pointer.md
  • @Gitee: https://gitee.com/cpu_code
  • @Github: https://github.com/CPU-Code
  • @CSDN: https://blog.csdn.net/qq_44226094
  • @Gitbook: https://923992029.gitbook.io/cpucode/