news 2026/4/29 7:09:29

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
安卓HAL C++基础-智能指针

一、原始指针的“定时炸弹”

假设你写:

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释放对象。

示例:

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>()到底是啥?

它的签名类似:

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,所以你可以用它。

std::shared_ptr<HelloTest> helloTest = ndk::SharedRefBase::make<HelloTest>();

效果:

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

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

五、服务注册与生存保证

当你调用:

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 7:08:23

颠覆传统:用Mac Mouse Fix重新定义macOS鼠标体验的完整指南

颠覆传统&#xff1a;用Mac Mouse Fix重新定义macOS鼠标体验的完整指南 【免费下载链接】mac-mouse-fix Mac Mouse Fix - Make Your $10 Mouse Better Than an Apple Trackpad! 项目地址: https://gitcode.com/GitHub_Trending/ma/mac-mouse-fix 你是否曾经在macOS上使用…

作者头像 李华
网站建设 2026/4/29 7:04:27

Java Lambda表达式:从入门到实战全解析

Java Lambda 表达式详解&#xff1a;从入门到实战1. Lambda 表达式基础Lambda 表达式是 Java 8 引入的函数式编程核心特性&#xff0c;本质是匿名函数。其语法结构为&#xff1a;(参数列表) -> { 函数体 }单参数简化&#xff1a;当参数只有一个时可省略括号x -> x * x单行…

作者头像 李华
网站建设 2026/4/29 7:04:24

如何用3个步骤永久保存微信聊天记录:WeChatExporter完整指南

如何用3个步骤永久保存微信聊天记录&#xff1a;WeChatExporter完整指南 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 你是否曾因手机丢失、系统更新或误操作而丢失珍贵…

作者头像 李华