• 3.10 C++高级编程_Android强弱指针的实现与使用


    上节通过一个死锁的问题,引入了强弱指针的概念。

    本节来讨论Android强弱指针的实现与使用。

    轻量级智能指针

    先来回顾一下轻量级智能指针。

    首先定义了一个 LightRefBase类,它有一个私有成员 mCount,即所谓的引用计数。

    然后定义了一个Person类,Person类继承了LightRefBase类,这样Person类就同样有引用计数了。

    可以通过 sp 来使用Person类,sp是一个模板类,它有一个私有成员m_ptr,指向我们使用new Person() 创建出来的Person对象。

    在sp的构造函数中,通过调用incStrong函数,来增加引用计数;在sp的析构函数中,通过调用decStrong函数,来减少引用计数。

    当检测到引用计数为0时,就把m_ptr指向的对象释放掉。

    这就是轻量级智能指针。

    统一实现强指针/弱指针

    现在,我们来尝试统一实现强指针/弱指针

    之前是LightRefBase,现在去掉轻量Light,变成RefBase

    mCount变成mStrongmWeak,其中mStrong是强引用计数,和之前mCount的作用相同;mWeak则是弱引用计数。

    对于mStrong,有:

    • void incStrong();
    • void decStrong();

    对于mWeak,有:

    • void incWeak();
    • void decWeak();

    它们的结构应该是这样,mStrongmWeak是私有成员,void incStrong();等函数是公有成员。

    把上述的类抽象出来,定义一个类StrongWeakRef_type。

    1. class StrongWeakRef_type {
    2. private:
    3. int mStrong;
    4. int mWeak;
    5. public:
    6. void incStrong();
    7. void decStrong();
    8. void incWeak();
    9. void decWeak();
    10. };

    为了兼容以前的函数,将incStrong和decStrong放在RefBase类中。

    另外,在头文件中,我们不希望别人可以看到StrongWeakRef_type的私有成员,mStrong和mWeak;只需要将public成员开放出来即可。

    那么可以通过抽象类界面来实现,将StrongWeakRef_type类拆分成两部分:

    1. 固定接口;
    2. 变化的实现;

    抽象出一个接口类weakref_type,里面只放接口

    1. class Weakref_type {
    2. public:
    3. void incWeak();
    4. void decWeak();
    5. };

    抽象出一个实现类weakref_impl,它继承于weakref_type

    1. class Weakref_impl {
    2. private:
    3. int mStrong;
    4. int mWeak;
    5. };

    然后,把相对固定接口类weakref_type,放在头文件里面;把实现类weakref_impl,放在cpp文件里面。

    需要使用时只需要包含头文件即可,而cpp文件,则可以编译成

    在RefBase.h文件中,有一个RefBase类,它会包含weakref_impl类。

    需要注意的是,它包含的是指向weakref_impl类的指针

    那么,为什么包含的是指针而不是 weakref_impl 类本身呢?

    答:如果包含的是 weakref_impl 类,那么后续如果 weakref_impl 类的成员有变动(即空间分配有变动),那么也就意味着所有使用到utils\RefBase.h文件的应用都需要重新编译

    这样的话,改动的代价就太大了,而如果只是包含指针,指针只是一个地址,在32位机中只占据4个字节,即使 weakref_impl 类的结构有变动,RefBase的空间布局也不会有影响,还是一个32位的指针。

    weakref_impl类

    问:weakref_impl类是在哪里实现的?

    答:在安卓源码的RefBase.cpp文件中实现的,它有 mStrong,mWeak和一些其他参数。

    并且可以看到,weakref_impl类是从weakref_type中派生出来的,而weakref_type则是在头文件RefBase.h中定义的,它定义了一些接口供外部调用。

    Person类

    当我们使用Person类的时候,首先是定义,Person需要继承RefBase。

    1. class Person : public RefBase {
    2. ...
    3. };
    • 使用强指针时,使用 sp; 对应的计数函数就是 incStrong函数和decStrong函数;
    • 使用弱指针时,使用 wp; 对应的计数函数就是 incWeak函数和decWeak函数;

    使用强指针

    创建一个include目录,从安卓源码把cutils和utils复制过来,放到include目录下。

    再把安卓源码中的RefBase.cpp也复制过来。

    写一个Makefile来编译代码,在Makefile中指定头文件路径。

    最终测试代码如下,中间分析过程省略,后面会提供指令下载可以编译的代码。

    1. #include <iostream>
    2. #include <string.h>
    3. #include <unistd.h>
    4. #include <utils/RefBase.h>
    5. using namespace std;
    6. using namespace android;
    7. class Person : public RefBase {
    8. private:
    9. sp<Person> father;
    10. sp<Person> son;
    11. public:
    12. void setSon(sp<Person> &other) { this->son = other; }
    13. void setFather(sp<Person> &other) { this->father = other; }
    14. Person()
    15. {
    16. cout << "Person()" << endl;
    17. }
    18. ~Person()
    19. {
    20. cout << "~Person()" << endl;
    21. }
    22. void printInfo(void)
    23. {
    24. cout << "printInfo()" << endl;
    25. }
    26. };
    27. void test_func(void)
    28. {
    29. sp<Person> father = new Person();
    30. sp<Person> son = new Person();
    31. father->setSon(son);
    32. son->setFather(father);
    33. }
    34. int main(int argc, char **argv)
    35. {
    36. test_func();
    37. return 0;
    38. }

    由于使用的是sp,即强指针,所以此时编译会出现和之前一样内存泄漏的问题。

    使用弱指针

    修改为弱指针wp,看一下是否可以解决这个问题。

    编译测试,发现析构函数有被调用,即没有发生死锁。

    修改main函数,在main函数中调用Person类的printInfo函数。

    编译测试,出现报错。

    出错的原因在于,对于弱指针,它没有重载'->'

    因为wp只是引用而已,并没有控制对象的生死,所以可能在引用时,对象已经死亡了

    同理,弱指针没有重载 '*'

    那么,弱指针要怎么使用'->' 和 '*' 呢?

    答:将弱指针wp转换为强指针sp之后,就可以使用'->' 和 '*' 了。

    wp类中有一个成员函数promote,使用这个函数可以将弱指针转换为强指针

    需要注意的是,promote函数可能转换成功,也可能转换失败,转换失败时返回0。

    所以,要对返回值做一个判断,只有转换成功时才能使用。

    此时编译测试,就没有问题了。

    修改代码,在Person类中增加一个私有成员name,一个构造函数Person(char *name),获取name的成员函数char *getName(void)。

    修改printInfo函数,将father和son的名字输出。

    修改测试函数test_func,分别设置和打印信息。

    在main函数中调用test_func函数。

     

    编译测试,此时可以输出son和father的名字,并且析构函数有被调用。

    总结

    最后,根据上面的测试可以知道,使用弱指针可以解决强指针中存在的死锁的问题,但是对于弱指针,如果想要调用Person里面的成员函数,必须先使用promote函数将弱指针转换为强指针。

    同时需要判断一下返回值,因为弱指针只是简单的返回某个对象而已,它不保证这个对象是否没有消亡。

    本节只是简单测试强弱指针的使用方法,对于它们的具体实现方法,待以后深入研究。

    获取源码

    本节代码,可以使用以下指令获取:

    1. git clone https://github.com/weidongshan/cpp_project.git
    2. git pull origin master
    3. git checkout v21
  • 相关阅读:
    Java内存模型(JMM)详解
    【游戏建模全流程】在Maya中制作赛博朋克风格场景
    高并发 -- 多级缓存
    Nvm的使用
    开发中-唯一标识符最佳做法
    糖尿病性视网膜病变(DR)的自动化检测和分期
    基于多个openEuler物理机执行mugen测试脚本
    【c++百日刷题计划】 ———— DAY20,刷题百天,养成刷题好习惯
    美容院管理系统有哪些促销方式?
    Java --- JVM类加载器
  • 原文地址:https://blog.csdn.net/qq_33141353/article/details/126560162