智能指针

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

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

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

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

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

image-20200716201143365

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

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

image-20200716204449025

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

image-20200716202227045

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

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

image-20200716201908168

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

image-20200712144527052

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

image-20200716203159554

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

image-20200716204100483

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

image-20200712145507418

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

轻量级指针

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

实现原理分析

分析LightRefBase类的实现原理

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

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

sp类的析构函数的实现 :

应用实例分析

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

lightpointer.cpp :

Android.mk

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

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

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

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

强指针和弱指针

强指针弱指针通过强引用计数和弱引用计数来维护对象的生命周期。

如果一个类的对象要支持使用强指针弱指针,那么它就必须从RefBase类继承下来,因为RefBase类提供了强引用计数器弱引用计数器

强指针的实现原理分析

分析RefBase类的实现原理 :

weakref_impl类同时为对象提供了强引用计数和弱引用计数 :

RefBaseweakref_typeweakref_impl类的关系 :

RefBase、weakref\_type和weakref\_impl类的关系

每一个RefBase对象都包含了一个weakref_impl对象, 而后者继承了weakref_type

强指针的实现类为spsp类的构造函数的实现 :

强指针类sp的构造函数的实现 , 主要做的事情就是增加对象的强引用计数和弱引用计数。也可看出, 目的是增加对象的强引用计数,但是同时也会增加对象的弱引用计数, 即一个对象的弱引用计数一定是大于等于它的强引用计数的。

sp类的析构函数的实现 :

对对象的生命周期控制方式作一个小结 :

如果一个对象的生命周期控制标志值 == 0, 那么只要它的强引用计数值 == 0, 系统就会自动释放这个对象

如果一个对象的生命周期控制标志值 == OBJECT_LIFETIME_WEAK, 那么只有当它的强引用计数值 和 弱引用计数值都 == 0, 系统才会自动释放这个对象。

如果一个对象的生命周期控制标志值被设置为 OBJECT_LIFETIME_FOREVER, 那么系统就永远不会自动释放这个对象, 它需要由开发人员来手动地释放

弱指针的实现原理分析

如果一个类的对象支持使用弱指针, 那么这个类就必须要从RefBase类继承下来, 因为RefBase类提供了弱引用计数器。

弱指针wp的实现 :

wp类的构造函数的实现 :

wp类的析构函数的实现 :

应用实例分析

在external目录下建立一个C++应用程序weightpointer来说明强指针和弱指针的使用方法

目录结构 :

weightpointer.cpp

Android.mk

编译成功之后, 就可以在out/target/product/gerneric/system/bin目录下看到应用程序文件weightpointer了; 打包成功之后, 该应用程序就包含在out/target/product/gerneric目录下的Android系统镜像文件system.img

Last updated

Was this helpful?