指针本身不构成响应式,需配合观察者模式和状态通知;裸指针无法自动感知变化,响应逻辑须由包装类(如observable)实现,通过shared_ptr管理生命周期、weak_ptr避免悬空与循环引用。

指针本身不构成响应式,需要配合观察者模式和状态变更通知
纯裸指针(int*、MyClass*)无法自动感知值变化,更不会触发回调。所谓“用指针实现响应式”,实际是用指针管理可观察对象的生命周期,并在赋值/修改时手动插入通知逻辑。C++ 没有语言级响应式支持(不像 Swift 的 @Published 或 Rust 的 Signal),必须靠显式设计。
典型做法是:定义一个包装类(如 Observable<t></t>),内部持有 T 值和回调列表;所有写操作都走 set() 方法,触发 notify();外部通过指针(Observable<int>* </int>)持有它——指针只负责间接访问和延长生命周期,真正的响应逻辑在类内部。
如何避免 raw pointer 导致的悬空与重复通知
直接传裸指针给观察者极易出错:被观察对象析构后指针变悬空;多个指针指向同一对象却各自注册回调,导致重复执行。
- 改用
std::shared_ptr<observable>></observable>管理所有权,观察者也持weak_ptr避免循环引用 - 在
Observable析构时清空回调列表(m_observers.clear()),防止后续误调用 - 注册回调时检查
weak_ptr.lock()是否有效,无效则跳过 - 不要让裸指针参与通知链——例如禁止把
&obj.value这种地址传给监听器
std::function + 指针组合的最小可行响应式示例
下面是一个仅依赖标准库的极简实现,重点看指针怎么用、哪里不能乱用:
class ObservableInt {
int m_value = 0;
std::vector<:function>> m_observers;
<p>public:
void set(int v) {
if (m_value != v) {
m_value = v;
for (auto& cb : m_observers) cb(m_value); // 通知
}
}</p>
<pre class="brush:php;toolbar:false;">void observe(std::function<void> cb) {
m_observers.push_back(cb);
}</void>
};
// ✅ 正确:用 shared_ptr 管理生命周期,观察者不持有裸指针
auto obs = std::make_shared // ❌ 危险:下面这行如果 obs 提前释放,cb 再调用就崩溃
// int* raw = &obs->m_value; // 别这么干
为什么不用 std::reference_wrapper 或普通引用替代指针
引用(ObservableInt&)无法重绑定,不能用于容器或异步回调上下文;std::reference_wrapper 虽可拷贝但不管理生命周期——一旦原对象销毁,它变成悬空引用,行为未定义。
而 shared_ptr 明确表达“我需要这个对象活着”,weak_ptr 显式处理“它可能已死”。指针在这里不是为了性能,而是为了可控的所有权语义。真正容易被忽略的是:响应式逻辑的边界必须和内存生命周期对齐,否则再多的回调注册也没意义。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











