Binder学习笔记(十一)—— 智能指针

轻量级指针

Binder的学习历程爬到驱动的半山腰明显感觉越来越陡峭,停下业务层的学习,补补基础层知识吧,这首当其冲的就是智能指针了,智能指针的影子在Android源码中随处可见。打开frameworkds/rs/cpp/util,RefBase.h和StrongPointer.h两个文件,代码多读几遍都能读懂,可是串起来总感觉摸不到骨架,把不住主线。闭上眼零零星星的点串不成一条线。究其原因应该是此处使用了模式,最好先剔除掉业务层的皮肉,把模式的骨架摸个门清,再回来看代码就会势如破竹了。

不是多么高深的设计模式,智能指针和引用计数的混合而已。但,不要轻敌。翻开书,对这两个模式的描述竟有50页之多。我读的是Scott Meyers的《More Effective C++》,侯sir翻译的版本,十年前读这本书的时候囫囵吞枣很多读不懂,有一些概念依稀留下点印象。这些印象就构成了日后遇到问题时的路标,告诉我答案在哪里。这本书条款28、29讲的正是智能指针和引用计数,两章结尾处的代码几乎就是Android源码中LightRefBase和sp的原型。每一个条款都从一个简单的目标入手,不断地解决问题,再升级提出它的不足,再找答案,直到把这个问题打穿打透为止。这两个条款的内容就不赘述了,书里写的更精彩。接下来我把模式和Android源码的智能指针对接起来。

《More Effective C++》第203页,条款29描绘了具有引用计数功能的智能指针模式图如下:
《More Effective C++》条款29
这张图还是把业务逻辑和模式框架混在一起了:
* String代表业务逻辑
* RCPtr是具备引用计数功能的智能指针
* RCObject用来履行引用计数的职责
* StringValue和HeapMemory又是局部的业务逻辑了

所以该模式本质上是由RCPtr和RCObject联袂完成,RCPtr负责智能指针,RCObject负责引用计数,如下:
具备引用计数功能得智能指针
一个对象如果要配备引用计数和智能指针,则需要:

class MyClass : public RCObject // 让该对象的类从RCObject派生
{
  ...};
RCPtr<MyClass> pObj; // 声明对象

对应到Android源码中,LightRefBase就是RCObject,sp就是RCPtr:
Android源码中的轻量级智能指针

我在初读Android源码的时候一直琢磨为什么搞得这么复杂,不能把智能指针封装在一个基类里。如果可以的话,MyClass只需要从这个类派生就好了。答案是不可以。因为RCPtr本质上要充当指针的角色,ptr1可以指向A,也可以指向B,当从A转向了B,应该让A的引用计数递减,让B的引用计数递增,这个计数只能是被指对象的属性,而不能是指针的。

我之所以会有合二为一的年头,是因为过去接触的智能指针大都是为了解决遇到Exception或错误返回时防止内存或资源泄露,又不想使用goto语句,比如:

int fun(char * filename)
{
    FILE* fp = fopen(filename, "r");
    int result = 0
    ... ...
    if(error){
        result = -1;
        goto exit;
    }
exit:
    fclose(fp);
    return result;
}

如果在函数中打开了多种资源,则要么记住它们的状态,在exit中根据状态擦屁股;要么就得有多个goto标记。此时就可以使用智能指针的思想,给文件做个封装:

void fun(char * filename)
{
    MyFile file;  // MyFile在析构的时候会自动关闭文件
    if(!file.open(filename, "r"))
        return -1;
    ... ...
}

这一类的智能指针可以看做“具备引用计数的智能指针”的特例,它的引用计数最多为1,且不存在所有权的转移,可以把引用计数RCObject模块退化掉,指针指向资源即代表引用计数为1,不指向任何资源则代表引用计数为0。因此这种情况可以让MyClass仅派生自RCPtr基类即可,一旦引用计数允许大于1,就必须带上RCObject的角色了。

回到Android源码上来,轻量级的智能指针:
* LightRefBase负责维护引用计数,并提供递增/递减的接口。
* sp履行智能指针的角色,负责构造析构、拷贝和赋值、提领。

还有一个问题:LightRefBase和《More Effective C++》条款29中的RCObject相比多出一个模板参数,在该类的定义中几乎没有用到这个模板参数,这是为什么?我分析应该是出于性能的考虑——这样做可以省去虚表的开销:

RCObject::removeReference()
{
  if(--refCount == 0) delete this;}

这里要经由基类的指针删除派生类的对象,在《Effecive C++(第三版)》(刚刚发现这本书的第二版和第三版调整很大!)第7条中说到:

当派生类的对象经由基类指针被删除时,基类必须有虚析构函数,否则会导致未定义的行为,通常是对象的devrived成分没被销毁。

RCObject确实声明了析构函数为virtual,也因此不得不引入虚表。再看LightRefBase:

template <class T>
class LightRefBase
{
... ...
    inline void decStrong(const Void* id) const{
        if(android_atomic_dec(&mCount) == 1){
            // 这里并没有delete this,而是先转成子类再delete,这就不再是
            // “经由基类指针删除子类对象”,而是“由子类指针删除子类对象”了,
            // 怎么得到子类指针?模板参数T呀!为这段代码拍案叫绝!
            delete static_cast<const T*>(this);
        }
    }
};

情景分析

Android智能指针的代码不多,且比较独立,我把它们抽取出来,再写一些测试用例,对这块代码的理解大有裨益。我在Anrdoid源码中每个函数头部都打了Log,标示函数名。代码可以到这里下载androidex/host-smartptr。这里的Android源码取自android-6.0.1_r11。
* StrongPointer.h
来自frameworks/rs/cpp/util/StrongPointer.h
* RefBase.h
来自frameworks/rs/cpp/util/RefBase.h
* RefBase.cpp
来自system/core/libutils/RefBase.cpp
* meyers.h
来自《More Effective C++》条款29,是带有引用计数功能的智能指针的实现
* testlightptr.cpp和testweightptr.cpp
是对Android轻量级智能指针和强、弱智能指针的测试用例,
* logger.h
是一个log工具,
* smartptr.cpp
是主入口函数,该文件包含若干测试用例,函数名为tc01、tc02…

该程序的使用方法为:

smartptr <tcname>

例如:

smartptr tc01  # 它执行例程函数tc01

一下是对轻量级指针的测试代码:

#include <stdio.h>
#include "RefBase.h"
#include "logger.h"

class LightClass : public LightRefBase <LightClass>
{
public:
    LightClass(){}
    ~LightClass(){}
};

int testlightptr(int argc, char const * argv[])
{
    // 初始lpLightClass的引用计数为0
    LightClass * pLightClass = new LightClass();
    // 调用sp的复制构造函数sp::sp(T* other),使得pLightClass的引用计数为1。
    sp<LightClass> lpOut = pLightClass;
    Logging("Light Ref Count: %d.", pLightClass->getStrongCount());
    {
        // 调用sp的赋值构造函数sp::sp(const sp<T>& other),
        // 使得pLightClass的引用计数累加为2
        sp<LightClass> lpInner = lpOut;
        Logging("Light Ref Count: %d.", pLightClass->getStrongCount());
        // lpInner析构,pLightClass的引用计数递减为1
    }
    Logging("Light Ref Count:%d.", pLightClass->getStrongCount());
    return 0;
    // lpOut析构,pLightClass引用计数递减为0,在decStrong(...)中delete pLightClass
}

执行结果如下:

$ ./smartptr tc01
[StrongPointer.h:
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值