安卓HAL C++基础-智能指针

一、原始指针的"定时炸弹"

假设你写:

cpp 复制代码
HelloTest* p = new HelloTest();   // 在堆上分配内存
p->getTestOne(...);
delete p;   // 必须手动释放

问题:

  • 如果中间某段代码提前 return -1,delete 就被跳过了 → 内存泄漏。

  • 如果有多个地方保存了这个指针,谁负责 delete?什么时候删?删了以后别人再用 → 悬空指针 → 崩溃。

二、std::shared_ptr:自动回收的"共享指针"

C++11 引入了 std::shared_ptr<T>,它是一个模板类,内部有两个东西:

  1. 原始指针:指向你分配的对象。

  2. 控制块 :包含一个引用计数 ,记录有多少个 shared_ptr 指向同一个对象。

规则:

  • 每多一个 shared_ptr 指向对象,计数 +1。

  • 当一个 shared_ptr 销毁(或不再指向该对象),计数 -1。

  • 当计数归零,自动执行 delete 释放对象。

示例:

cpp 复制代码
std::shared_ptr<HelloTest> sp1 = std::make_shared<HelloTest>();
// 引用计数 = 1
std::shared_ptr<HelloTest> sp2 = sp1;
// 引用计数 = 2
sp1.reset();  // 计数 -1 → 1,对象还在
sp2.reset();  // 计数 -1 → 0,对象自动销毁,你不需要 delete

这样你永远不用手动 delete,安全又省心。

三、为什么不用 std::make_shared 创建 HAL 服务?

在 HAL 服务中,我们的 HelloTest 对象需要通过 binder 传递给其他进程。Binder 驱动内部也需要对这个对象进行跨进程的引用计数,确保当所有客户端都断开连接后,服务对象才能安全销毁。

普通的 std::shared_ptr 只维护进程内 的引用计数,binder 不知道它。如果客户端通过 binder 持有了引用,而服务端这边 shared_ptr 计数归零销毁了对象,客户端就会拿到一个悬空指针,导致系统崩溃。

因此,Android NDK 提供了一个特殊的基类:ndk::SharedRefBase 。

继承自它的类,内部实现了 binder 需要的 incStrong/decStrong 接口,这样 binder 驱动就能与 C++ 智能指针的引用计数协同工作。

四、ndk::SharedRefBase::make<T>() 到底是啥?

它的签名类似:

cpp 复制代码
template<typename T>
static std::shared_ptr<T> make(Args&&... args);

这相当于一个工厂函数,它做了:

  1. 调用 new T(args...) 创建对象。

  2. 将该对象包裹在一个 std::shared_ptr<T> 里,但关键的是,这个 shared_ptr 使用的删除器 和控制块 是 SharedRefBase 定制的,使得 binder 的引用计数与 shared_ptr 的引用计数打通。

  3. 返回这个 shared_ptr。

因为你的 HelloTest 继承自 BnHelloTest,而 BnHelloTest 最终继承自 ndk::SharedRefBase,所以你可以用它。

cpp 复制代码
std::shared_ptr<HelloTest> helloTest = ndk::SharedRefBase::make<HelloTest>();

效果:

  • 获得一个安全的智能指针 helloTest,管理 HelloTest 对象的生命周期。

  • 该对象能够安全地通过 helloTest->asBinder() 注册到 binder 驱动,并被客户端远程持有。

五、服务注册与生存保证

当你调用:

cpp 复制代码
AServiceManager_addService(helloTest->asBinder().get(), desc.c_str());

vndservicemanager 会保留一个 binder 引用,这会增加 binder 侧的引用计数。

此时:

  • 进程内的 shared_ptr 计数至少为 1(helloTest 本身)。

  • binder 驱动也持有对该对象的引用,防止它在所有客户端断开前被销毁。

当 main 函数里的 helloTest 最终离开作用域(比如进程退出)时,shared_ptr 计数递减。但如果系统中仍有客户端连接到该服务,binder 侧的引用计数仍大于 0,对象会一直存活,直到所有客户端断开连接,binder 引用归零,然后才触发真正的析构。

这就是"能在跨进程传递时正确管理生命周期"的含义。

六、直观对比

创建方式 适用场景 能否跨进程安全引用
new HelloTest + delete 普通 C++ 对象 ❌ 不安全
std::make_shared<HelloTest>() 普通 C++ 对象,进程内共享 ❌ binder 不认可其计数
ndk::SharedRefBase::make<HelloTest>() HAL 服务对象 ✅ binder 与 shared_ptr 协同计数

七、通俗类比

把 std::shared_ptr 想象成一个自动回收的抽水马桶 :当屋里没人时自动冲水。
ndk::SharedRefBase::make 则更进一步,它把隔壁房间(binder 驱动)的人也能算进去,只有当两个房间的人都走了,马桶才会冲。

这样,你的 HAL 服务就不会因为服务端代码的一个 } 而突然消失,导致正在呼叫它的客户端崩溃。

相关推荐
倒头就睡的小比特2 天前
算法竞赛C++常用的STL
c++·算法
weilx12342 天前
C++笔记-文件IO-<fcntl.h>
c++
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
Smileyqp沛沛3 天前
前端?C++ ?较大差异基础罗列
c++·基础·前端转c++
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
吞下星星的少年·-·3 天前
C++ 萌新语法入门篇
c++·算法比赛
霍霍的袁3 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
another heaven3 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法