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

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

假设你写:

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

问题:

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

  • 如果有多个地方保存了这个指针,谁负责 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 服务就不会因为服务端代码的一个 } 而突然消失,导致正在呼叫它的客户端崩溃。

相关推荐
C++ 老炮儿的技术栈38 分钟前
基于Qt实现轻量化本地音乐播放器
开发语言·c++·qt·c·播放器·音乐
qq_4480111640 分钟前
C语言中的柔性数组
c语言·开发语言·柔性数组
小白学大数据43 分钟前
长周期爬虫的数据一致性:断点续爬 + 事务回滚保障采集质量
开发语言·爬虫·测试工具
zlinear数据采集卡1 小时前
D223上位机C#开发实战:从帧解析到波形显示的完整实现
开发语言·arm开发·嵌入式硬件·fpga开发·开源·c#
Poo_Chai2 小时前
QT emit信号后完整处理流程,包括槽函数响应流程
java·开发语言·数据库
瑞码空间2 小时前
Java 图形界面(GUI)完整知识点手册
java·开发语言·图形界面·swing
小努蛋2 小时前
linux编译openresty异常问题,lua_cjson.c:706:5: 错误: ‘for‘ 循环初始化声明只在 C99 或 C++ 模式下允许
开发语言·lua·openresty
qz_Serene2 小时前
C++:类和对象(上)
开发语言·c++
西安景驰电子3 小时前
《PTP精确时间协议系列》第一篇:从原理到应用,全面解读IEEE 1588
linux·运维·服务器·开发语言·网络·windows·php
ctlover3 小时前
Python文件操作
开发语言·python