安卓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 服务就不会因为服务端代码的一个 } 而突然消失,导致正在呼叫它的客户端崩溃。

相关推荐
布莱克60514 分钟前
C/C++ 中变量的声明与定义:区别、联系与常见误区
c语言·开发语言·c++·声明·定义
LabVIEW开发24 分钟前
LabVIEW 局部变量为何引发竞争条件?四种同步机制
开发语言·labview
benchmark_cc1 小时前
A 股、港股、美股如何建立统一的标的主数据格式?
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
Zenova EdgeOS2 小时前
Go 工业边缘错误处理实战:用 errors 构建可追溯错误链
开发语言·数据库·golang
取名真是2 小时前
Python 算法知识点从基础到进阶
开发语言·python·算法
FRing033 小时前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Fatbobman(东坡肘子)4 小时前
快速消失的硬盘容量 -- 肘子的 Swift 周报 #160
开发语言·人工智能·redis·ios·swiftui·iphone·swift
揽星河..4 小时前
【C语言】底层程序设计
c语言·开发语言
JavaPub-rodert4 小时前
从零搭建通用管理后台:ShiyuAdmin 的 Go、React 与 RBAC 实践
开发语言·react.js·golang
沫璃染墨5 小时前
从零入门计算机网络系列(十二):HTTP长连接与会话管理详解——从Connection到Cookie与Session
linux·服务器·c++·tcp/ip·ubuntu·http·交互