C++: 智能指针 sp 之 Android强弱指针的实现与使用

回顾下前面提到的Android 轻量级智能指针 LightRefBase:

class Person : public LightRefBase<Person>{}

sp<Person>{
.Person *mptr;
sp(){
incStrong;
}
~sp(){
decStrong;
}
}

类比,强弱指针可能会有如下统一实现:

class RefBase{
private:
    int mStrong;
    int mWeak;

Public:
    void incStrong();
    void decStrong();
    void incWeak();
    void decWeak();
}

进一步抽象剥离:

StrongWeakRef_type mRefs;

class StrongWeakRef_type{
private:
    int mStrong;
    int mWeak;

Public:
    //void incStrong(); //为了保持兼容,这两个方法保留在class RefBase中
    //void decStrong();
    void incWeak();
    void decWeak();
}

实际开发中涉及到多个模块相互调用时,使用者常常是引用对方的头文件,使用者在头文件中往往不想看到private属性的mStrong和mWeak,只想知道可以调用的Public接口。我们想到的优化方法是,将一个类拆成两部分:1)固定接口类;2)容易变化的实现类。具体如下:

//抽象为接口类。放在头文件中。
class weakRef_type{
public:
    void incWeak();
    void decWeak();
}

//独立为实现类。放在cpp文件中。
class weakRef_impl : public weakRef_type {
    int mStrong;
    int mWeak;
}

开发中,cpp文件可以编译成库文件,使用者可以使用头文件和库文件即可。
进一步推测,class RefBase部分内容如下:

class RefBase{
/* 为什么不能是包含成员变量而是包含指针变量呢?
  * 因为一旦weakRef_impl的实现发生变化,比如新增和减少内容,
  * 会导致包含它的所有者的内存布局也发生变化,而仅包含指针的话没有这个问题。
  */
//weakref_impl mRefs;
weakref_impl * mRefs;
}

看一下Android对统一的强弱指针的实现–class RefBase,验证一下:

//frameworks/rs/cpp/util/RefBase.h
template <typename T>
class wp
{
...
private:
...
    weakref_type*   m_refs;
}

//system/core/libutils/RefBase.cpp
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;
    volatile int32_t    mWeak;
...
}

使用Anroid强弱指针:

class Person: public RefBase{}

//强指针
sp<Person>{
sp(){incStrong();}
~sp(){decStrong();}
}

//弱指针
wp<Person>{
wp(){incWeak();}
~wp(){decWeak();}
}

使用上一节“Android 弱指针的引入”的例子,将使用轻量级智能指针改成使用强指针:

//class Person : public LightRefBase{
class Person : public RefBase<Person>{

private:
    sp<Person> father;
    sp<Person> son;
。。。
}

验证结果也一样–目标对象在main函数/test_func函数调用结束时没有被销毁。
将强指针改成弱指针,此问题解决:

//class Person : public LightRefBase{
class Person : public RefBase<Person>{

private:
    wp<Person> father;
    wp<Person> son;
。。。
}

进一步看下强弱指针的使用:

int main(int argc, char **argv)
{    
    wp<Person> s = new Person();
    //s->printInfo(); /* 出错, 注意:wp没有重载"->", "*" */
    //(*s).printInfo(); /* 出错, 注意:wp没有重载"->", "*" */

    sp<Person> s2 = s.promote(); //弱指针升级成强指针
    if (s2 != 0) {
        s2->printInfo();
    }

    return 0;
}

再看个例子:

#include 
#include 
#include 
#include 

using namespace std;
using namespace android;

class Person : public RefBase {

private:
    char *name;
    
    wp<Person> father;
    wp<Person> son;

public:
    Person() {
        cout <<"Pserson()"<<endl;
    }

    Person(char *name) {
        cout <<"Pserson(char *name)"<<endl;
        this->name = name;
    }

    ~Person()
    {
        cout << "~Person()"<<endl;
    }

    void setFather(sp<Person> &father)
    {
        this->father = father;
    }

    void setSon(sp<Person> &son)
    {
        this->son = son;
    }

    char *getName(void)
    {
        return name;
    }
    
    void printInfo(void)
    {
        sp<Person> f = father.promote();//(1)
        sp<Person> s = son.promote();
        
        cout<<"I am "<<name<<endl;

        if (f != 0)//(2)
            cout<<"My Father is "<<f->getName()<<endl;

        if (s != 0)
            cout<<"My Son is "<<s->getName()<<endl;
    }
};

void test_func()
{
    sp<Person> father = new Person("LiYiShi");
    sp<Person> son = new Person("LiErShi");

    father->setSon(son);

    son->setFather(father);

    father->printInfo();

    son->printInfo();
}

int main(int argc, char **argv)
{    
    test_func();
    return 0;
}

可以看到,使用弱指针wp就不会导致相互引用时产生死锁导致双方都不能释放对象的问题。但是对于弱指针wp,若想调用目标对象中的某些方法时,比如像(1)使用promote方法把它升级为强指针;且(2)升级完后要判断返回值,因为弱指针只是简单的引用某个对象而已,并不保证对象是否消亡。另外,Android 强指针的实现原理与前面讲的轻量级智能指针是类似的。

若要继续分析Android实现强弱指针的源码,需要注意如下两点:

1)mFlags:
OBJ_LIFETIME_STRONG:表示对象生命周期由mStrong决定;
OBJ_LIFETIME_WEAK:表示对象生命周期由mWeak决定;
2)mStrong <= mWeak


参考:韦东山C++教程

你可能感兴趣的:(C/C++,RefBase)