一、原始指针的“定时炸弹”
假设你写:
HelloTest* p = new HelloTest(); // 在堆上分配内存 p->getTestOne(...); delete p; // 必须手动释放问题:
如果中间某段代码提前
return -1,delete就被跳过了 →内存泄漏。如果有多个地方保存了这个指针,谁负责
delete?什么时候删?删了以后别人再用 →悬空指针→ 崩溃。
二、std::shared_ptr:自动回收的“共享指针”
C++11 引入了std::shared_ptr<T>,它是一个模板类,内部有两个东西:
原始指针:指向你分配的对象。
控制块:包含一个引用计数,记录有多少个
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);这相当于一个工厂函数,它做了:
调用
new T(args...)创建对象。将该对象包裹在一个
std::shared_ptr<T>里,但关键的是,这个shared_ptr使用的删除器和控制块是SharedRefBase定制的,使得 binder 的引用计数与shared_ptr的引用计数打通。返回这个
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 服务就不会因为服务端代码的一个}而突然消失,导致正在呼叫它的客户端崩溃。