Android 智能指针详解 -- sp

源码基于:Android 7.0

相关博文:

Android 智能指针详解

Android 智能指针详解 -- wp 

Android 智能指针详解 -- sp

Android 智能指针详解 -- RefBase

 

 

0. 前言

Android中定义了两种智能指针类型,一种是强指针sp(strong pointer),另外一种是弱指针(weak pointer)。其实称之为强引用和弱引用更合适一些。强指针与一般意义的智能指针概念相同,通过引用计数来记录有多少使用者在使用一个对象,如果所有使用者都放弃了对该对象的引用,则该对象将被自动销毁。
弱指针也指向一个对象,但是弱指针仅仅记录该对象的地址,不能通过弱指针来访问该对象,也就是说不能通过弱智真来调用对象的成员函数或访问对象的成员变量。要想访问弱指针所指向的对象,需首先通过wp类所提供的promote()方法将弱指针升级为强指针。弱指针所指向的对象是有可能在其它地方被销毁的,如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况。

1. 源码剖析

1.1 sp 的定义

system/core/include/utils/StrongPointer.h

template<typename T>
class sp {
public:
    inline sp() : m_ptr(0) { }

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

    ~sp();

    // Assignment

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

    template<typename U> sp& operator = (const sp<U>& other);
    template<typename U> sp& operator = (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;
    void set_pointer(T* ptr);
    T* m_ptr;
};

从代码中我们可以得知: 

  • 这是个模板类

对于模板,详细看 C++模板详解

  • 类型T、U是指针变量的类型
  • 有7个构造函数、6个“=”的重载
  • 宏COMPARE的6个函数都是运算符的重载
  • m_ptr就是指针变量,后面的所有操作都是通过这个来

1.2 sp 构造函数实现

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other) //如果构造函数形参的指针不为null,该指针的计数加1,这个后面解释
        other->incStrong(this);
}

template<typename T>
sp<T>::sp(const sp<T>& other)//实参为sp<T>类型的对象,这里形参为引用
        : m_ptr(other.m_ptr) {
    if (m_ptr)
        m_ptr->incStrong(this);
}

template<typename T>
sp<T>::sp(sp<T>&& other) //这里C++11中的移动构造函数,会在另一篇博文中详解
        : m_ptr(other.m_ptr) {
    other.m_ptr = nullptr;
}

template<typename T> template<typename U>
sp<T>::sp(U* other) //这里大部分是子类往父类转换
        : m_ptr(other) {
    if (other)
        ((T*) other)->incStrong(this);
}

template<typename T> template<typename U>
sp<T>::sp(const sp<U>& other)
        : m_ptr(other.m_ptr) {
    if (m_ptr)
        m_ptr->incStrong(this);
}

template<typename T> template<typename U>
sp<T>::sp(sp<U>&& other)//同样是C++11特性移动构造函数,后面详解
        : m_ptr(other.m_ptr) {
    other.m_ptr = nullptr;
}

sp构造就是为了给指针T或者指针U多一个引用的地方,所以,在构造的时候必须做两件事情:

  • 初始化sp中关键的指针m_ptr,也就是实参的指针
  • m_ptr也就是实参指针必须要调用incStrong()进行计数加1

构造的时候原来sp的引用为0,无需对m_ptr进行decStrong(),但下面的运算符重载就不是这样了。

对于incStrong 和 decStrong暂时理解为指针m_ptr的计数加1和减1,后面会详解。

1.3 sp 中的赋值运算符重载

template<typename T>
sp<T>& sp<T>::operator =(const sp<T>& other) {
    T* otherPtr(other.m_ptr);
    if (otherPtr)
        otherPtr->incStrong(this);//参数other中的指针m_ptr要多一个引用了,调用incStrong()
    if (m_ptr)
        m_ptr->decStrong(this);//当前的指针需要被other代替,那么目前的指针m_ptr的引用计数需要减1
    m_ptr = otherPtr;
    return *this;
}

template<typename T>
sp<T>& sp<T>::operator =(sp<T>&& other) {
    if (m_ptr)
        m_ptr->decStrong(this);//这里是移动赋值,会将other中的m_ptr直接移动给这里的m_ptr,之前要先decStrong
    m_ptr = other.m_ptr;
    other.m_ptr = nullptr;
    return *this;
}

template<typename T>
sp<T>& sp<T>::operator =(T* other) {
    if (other)
        other->incStrong(this);//直接指针赋值就更简单了
    if (m_ptr)
        m_ptr->decStrong(this);
    m_ptr = other;
    return *this;
}

template<typename T> template<typename U>
sp<T>& sp<T>::operator =(const sp<U>& other) {//考虑到子类向父类赋值
    T* otherPtr(other.m_ptr);
    if (otherPtr)
        otherPtr->incStrong(this);
    if (m_ptr)
        m_ptr->decStrong(this);
    m_ptr = otherPtr;
    return *this;
}

template<typename T> template<typename U>
sp<T>& sp<T>::operator =(sp<U>&& other) {
    if (m_ptr)
        m_ptr->decStrong(this);
    m_ptr = other.m_ptr;
    other.m_ptr = nullptr;
    return *this;
}

template<typename T> template<typename U>
sp<T>& sp<T>::operator =(U* other) {
    if (other)
        ((T*) other)->incStrong(this);
    if (m_ptr)
        m_ptr->decStrong(this);
    m_ptr = other;
    return *this;
}

6个赋值函数,或者叫6个 “=” 的重载中注意的是原来的decStrong 和新的指针的incStrong。

1.4 sp的析构函数

template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}

当sp不在使用的时候,指针T *m_ptr需要将计数减1。

1.5 三个获取指针的方式

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

例如:

class Test {
public:
    Test() {}
	~Test() {}
	void test() { cout << "just for test ...\n";}
};

main () {
    sp<Test> hehe(new Test()); //构造的时候可以直接传入指针
	hehe->test(); //通过 -> 指向指针m_ptr,直接调用Test的test函数
	*hehe.test(); //通过 * 找到对应的Test 引用
	Test *t = hehe.get(); //sp 中get函数就是为了获取指针m_ptr
	t->test();
}

1.6 其他的函数

template<typename T>
void sp<T>::force_set(T* other) {//特殊需要用,一般不会用到
    other->forceIncStrong(this);
    m_ptr = other;
}

template<typename T>
void sp<T>::clear() {//做sp rest功能使用,会将指针的计数减1,指针置为空指针,注意C++11上用nullptr
    if (m_ptr) {
        m_ptr->decStrong(this);
        m_ptr = 0;
    }
}

template<typename T>
void sp<T>::set_pointer(T* ptr) {//这个是private函数,wp在做promote的时候会使用到,wp通过friend方式调用到这里。
    m_ptr = ptr;
}

2. 遗留问题

  • 什么是wp
  • incStrong 和decStrong的具体实现是什么,为什么是控制指针的计数
     

带着这两个问题,看下一篇博文

 相关博文:

Android 智能指针详解

Android 智能指针详解 -- wp 

Android 智能指针详解 -- sp

Android 智能指针详解 -- RefBase

  • 13
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值