跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

C++ 实现轻量级观察者模式(RAII 风格封装)回调管理逻辑实现方案源码【源码】

observer_handle必须是move-only类型,以确保“订阅-退订”一对一绑定;拷贝构造/赋值需显式删除,移动后原句柄置空,防止重复erase或悬空引用。 直接用
std::function
+
std::vector
管理回调,不加 RAII 封装,基本等于裸写——生命周期错配、重复注册、野指针调用全靠人肉防。真要轻量又安全,必须把「订阅」和「自动退订」绑死在对象生命周期上。 为什么 observer_handle 必须是 move-only 类型 避免同一回调被多个 handle 持有,导致析构时重复 erase 或悬空引用。copyable 会破坏 RAII 的“一对一绑定”契约。
observer_handle
的拷贝构造/赋值运算符必须显式删除(
= delete
) move 构造后原 handle 置为无效态(如内部指针设为
nullptr
),防止二次析构 若允许 copy,
~observer_handle()
可能两次调用
erase
同一迭代器,UB callback_list 容器选 std::vector 还是 std::list 选
std::vector<:function>>
更合适,但关键不是容器类型,而是怎么存迭代器/句柄。 存
std::vector::iterator
不可行:插入/删除会失效;改用下标索引(
size_t
)更稳 每个回调注册时分配唯一
id
(如原子递增计数器),handle 持有该
id
,注销时按 id 查找并 swap-erase
std::list
虽迭代器稳定,但缓存不友好、内存开销大,对轻量级目标得不偿失 如何避免 notify 期间修改容器导致的迭代器失效 不能边遍历边
erase
,也不能在回调里调用
subscribe
/
unsubscribe
。 C知道 CSDN推出的一款AI技术问答工具 下载 立即学习 “ C++免费学习笔记(深入) ”; notify 前先拷贝一份活跃回调的
std::vector<:function>>
(只读快照) 回调执行全程不持有任何锁或引用原始容器,彻底隔离修改与通知 若需支持运行时动态增删,用双缓冲:当前 notify 用 buffer A,新增/删除操作进 buffer B,notify 结束后 swap 错误现象示例:
std::vector::erase(it++)
在回调里触发 erase → it 失效 → 下次 ++ UB RAII handle 的典型误用场景 handle 生命周期脱离了预期作用域,是最隐蔽的坑。 把 handle 存入全局
std::vector
或静态容器 → 对象析构后 handle 仍活着,析构时 erase 已销毁的 vector lambda 捕获了局部对象的引用,handle 存活但 lambda 捕获失效 → notify 时访问 dangling reference 跨线程传递 handle:未加同步就在线程 A 构造、线程 B 析构 → data race on internal id/index 正确做法:handle 必须和观察者对象同生共死;若需跨作用域,用
std::shared_ptr
管理观察者,handle 持有 weak_ptr 防循环引用 最易被忽略的是 notify 快照的拷贝成本——如果回调函数体本身很重,拷贝
std::function
对象可能比执行还慢;这时候得权衡:要么接受小对象拷贝,要么改用回调对象地址+虚函数表方案,但那就不是“轻量级”了。

相关文章