raii封装observerhandle实现自动注册注销,无锁weak_ptr队列避免竞态,模板eventchannel支持move-only句柄,静态函数指针表提供零开销方案,observer_ptr保障弱引用安全。

一、基于RAII的观察者注册与自动注销机制
在C++中,若观察者生命周期早于被观察对象销毁,手动解注册易引发悬空回调。RAII风格封装通过对象构造时注册、析构时自动注销,确保回调链表一致性。该方案将订阅关系绑定至局部作用域对象的生存期。
1、定义ObserverHandle类,内部持有一个std::shared_ptr
2、ObserverHandle构造函数接收一个std::function
3、在被观察者类中维护std::vector<:shared_ptr>> observers列表,每次调用subscribe时将新生成的ObserverHandle::token压入;
4、ObserverHandle析构时触发删除器,执行注销逻辑(如从observers中擦除对应项或标记为无效);
5、被观察者notify方法遍历时跳过已失效的shared_ptr(可通过weak_ptr过渡或原子标志位判断)。
二、无锁回调队列配合智能指针生命周期控制
为避免多线程下注册/注销与通知的竞态,采用std::weak_ptr包装观察者回调,通知时仅对未过期的weak_ptr.lock()成功者执行调用。所有回调存储于std::vector<:weak_ptr>>>,由观察者自身持有强引用。
1、声明Observer类,内部包含std::shared_ptr<:function>> callback_成员;
2、Subject类提供subscribe方法,接受std::shared_ptr
3、notify方法遍历callbacks_,对每个weak_ptr调用lock(),若返回非空shared_ptr则执行其指向的函数;
4、Observer对象离开作用域时,其内部callback_自动释放,对应weak_ptr在下次notify中失效,无需显式注销。
三、模板化事件通道配合move-only句柄
使用move-only ObserverHandle避免重复注销或多次析构风险,结合std::function和std::any实现类型擦除,使同一Subject可承载多种事件签名。句柄移动后原实例不可再用于注销操作。
1、定义template
2、EventChannel内部使用std::vector<:function>> handlers_及std::mutex保护;
3、subscribe返回一个move-only Handle类,其构造时将handler索引和引用计数指针传入,析构时调用erase_at(index);
4、Handle禁止拷贝,仅允许移动,移动后置内部指针为nullptr,防止二次析构;
5、Subject::notify(...)遍历handlers_,逐个调用,不检查有效性——因Handle析构即同步移除,保证容器始终一致。
四、静态局部变量+函数指针登记表的零开销方案
适用于全局唯一Subject且观察者均为无状态函数对象的场景。利用函数地址作为键,静态std::unordered_map存储回调映射,注销通过函数指针直接擦除,无堆分配、无虚函数、无引用计数开销。
1、声明static std::unordered_map
2、subscribe接收一个自由函数指针或lambda(需转为函数指针),以reinterpret_cast
3、unsubscribe接收相同函数指针,执行registry_.erase(key);
4、notify遍历registry_.values()并调用;
5、为支持捕获型lambda,提供额外重载:接受std::shared_ptr<:function>>并以其.get()地址注册,同时延长shared_ptr生存期至调用方作用域结束。
五、std::experimental::observer_ptr辅助的安全弱引用管理
当编译环境支持std::experimental::observer_ptr(或C++17后可用的std::observer_ptr),可替代raw pointer实现非拥有式观察,配合Subject内部的std::list<:function>>与迭代器失效安全擦除策略。
1、Subject内部维护std::list<:function>> handlers_;
2、subscribe返回一个封装了handlers_.insert()迭代器的ObserverToken类;
3、ObserverToken析构时调用handlers_.erase(iterator),因list迭代器不因其他元素增删而失效;
4、notify使用范围for遍历handlers_,无须检查有效性;
5、ObserverToken禁止拷贝,移动后原对象迭代器置为handlers_.end(),再次析构无动作。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











