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

相关推荐
还是阿落呀2 小时前
基本控制结构2
c++
啧不应该啊2 小时前
Day1 Python 与 C 的类型区别
c语言·开发语言
多思考少编码2 小时前
PAT甲级真题1001 - 1005题详细题解(C++)(个人题解)
c++·python·最短路·pat·算法竞赛
cen__y2 小时前
Linux07(信号01)
linux·运维·服务器·c语言·开发语言
xingpanvip2 小时前
星盘接口开发文档:星相日历接口指南
android·开发语言·前端·css·php·lua
guygg882 小时前
基于遗传算法的双层规划模型求解MATLAB实现
开发语言·matlab
凯瑟琳.奥古斯特3 小时前
SQLAlchemy核心功能解析
开发语言·python·flask
卷Java3 小时前
GPTQ vs AWQ vs GGUF:模型量化工具横向测评
开发语言·windows·python
极客智造3 小时前
C++ 标准 IO 流全详解:cin /cout/get /getline 原理、用法、区别与避坑
c++·io