ObserverHandle 通过裸指针绑定 EventContext 实现自动注销,禁用拷贝、移动语义安全,析构时 noexept 调用 remove_at 原子清空并收缩 vector;EventContext 以 vector 存储回调,fire 遍历执行非空函数。

在 C++ 项目中需要为事件系统提供自动生命周期管理的观察者注册机制,避免手动注销导致的悬空回调或重复注销崩溃,且不依赖智能指针或全局状态。
定义可自动注销的观察者句柄
声明一个轻量级句柄类 ObserverHandle,内部仅持有一个指向弱引用控制块的裸指针(非拥有型),构造时绑定到注册上下文,析构时触发注销逻辑。
该类默认移动语义禁用拷贝,确保每个句柄唯一对应一次注册;若意外拷贝,编译期报错比运行时悬空更安全。
ObserverHandle 的析构函数必须是 noexcept,否则 RAII 保证失效——【析构函数未声明 noexcept 将导致栈展开时调用 std::terminate】。
立即学习“C++免费学习笔记(深入)”;
实现注册上下文与回调存储容器
定义 EventContext 模板类,模板参数为回调签名,例如 void(int)。内部使用 std::vector<:function>> 存储活跃回调,并维护一个 std::atomic
注册函数 add_observer 接收 std::function 对象,将其压入 vector 尾部,返回一个 ObserverHandle 实例,该实例持有当前回调在 vector 中的索引位置和 context 的 this 指针。
注意:不能用 std::shared_ptr
实现安全注销逻辑
ObserverHandle 析构时,调用其持有的 EventContext* 的 remove_at(index) 方法。
remove_at 方法需原子性完成三件事:① 将目标槽位的 std::function 置为空;② 将 vector 末尾元素移到该槽位;③ 调用 pop_back() 缩容。这样避免迭代器失效,也无需锁。
方法一:使用 std::exchange(this->callbacks[index], {}) 清空原槽位,再 move 末尾元素覆盖它,最后 pop_back()。
方法二:先记录末尾索引,swap(callbacks[index], callbacks.back()),再 pop_back() —— 但 swap 后原槽位仍含有效函数对象,若此时有并发调用会误执行,故不推荐。
【必须确保 remove_at 中清空动作发生在移动/交换之前,否则可能执行已移走的回调】。
触发所有注册回调
EventContext 提供 fire() 方法,遍历 callbacks 容器,对每个非空 std::function 执行 operator()。
遍历时使用 size_t i = 0; i
每次调用前检查 if (callbacks[i]),跳过已被注销的槽位。
这一步操作起来很简单,直接遍历并调用即可,无需额外同步开销。


















