依赖类间状态一致性问题咨询(含Foo/Bar类代码示例)
这个问题的核心痛点很明确:Bar持有了Foo返回的State引用,但Foo的handle_event()会让旧的State失效并重建,这就导致Bar手里的引用直接悬空了——接下来操作这个引用的话,轻则行为异常,重则直接崩溃。下面我给你几个实用的解决方案,你可以根据自己的业务场景来选:
方案1:让Bar主动按需获取状态(最简单直接)
核心思路是不要让Bar长期持有State的引用,而是每次需要使用状态时,再从Foo那里主动获取最新的实例。这样完全避免了引用悬空的问题。
修改后的Bar代码示例:
class Bar { public: // 构造时只持有Foo的引用,不直接拿State Bar(const Foo& foo) : m_foo(foo) {} // 每次需要处理状态时,实时获取最新的State void process_state() { const State& current_state = m_foo.get_state(); // 这里放心使用current_state即可,它是当前Foo的有效状态 // ...业务逻辑... } private: const Foo& m_foo; // 持有Foo的引用而非State引用 };
这个方案的优点是实现成本极低,不需要改动Foo的结构;缺点是如果Bar需要频繁使用状态,每次获取可能会有微小的性能开销,但绝大多数场景下完全可以忽略。
方案2:观察者模式(实时同步首选)
如果Bar需要实时感知State的更新,那观察者模式是最适合的方案。让Foo维护一个观察者列表,当State更新时主动通知所有订阅的观察者(包括Bar),Bar收到通知后更新自己的状态引用。
首先修改Foo,添加观察者管理逻辑:
#include <vector> #include <algorithm> // 定义观察者抽象接口 class StateObserver { public: virtual void on_state_updated(const State& new_state) = 0; virtual ~StateObserver() = default; }; class Foo { public: const State& get_state() const; void handle_event(); // 注册/移除观察者 void register_observer(StateObserver* observer) { m_observers.push_back(observer); } void remove_observer(StateObserver* observer) { auto it = std::find(m_observers.begin(), m_observers.end(), observer); if (it != m_observers.end()) { m_observers.erase(it); } } private: // 状态更新后通知所有观察者 void notify_observers() { const State& latest_state = get_state(); for (auto observer : m_observers) { observer->on_state_updated(latest_state); } } std::vector<StateObserver*> m_observers; // 其他私有成员 }; // 修改handle_event,在状态更新后触发通知 void Foo::handle_event() { // 原有的失效并重建State的逻辑 // ... notify_observers(); // 通知所有观察者状态已更新 }
然后让Bar实现观察者接口:
class Bar : public StateObserver { public: Bar(Foo& foo) : m_foo(foo) { m_foo.register_observer(this); // 初始化时获取初始状态 m_state = m_foo.get_state(); } ~Bar() { // 销毁时从Foo的观察者列表移除,避免悬空指针 m_foo.remove_observer(this); } // 实现观察者接口,收到通知后更新状态 void on_state_updated(const State& new_state) override { m_state = new_state; } private: Foo& m_foo; const State& m_state; };
这个方案的优点是状态同步实时性强,适合对状态一致性要求高的场景;需要注意的是要管理好观察者的生命周期,避免Bar销毁后Foo还去通知它。
方案3:用智能指针替代裸引用(适合动态分配的State)
如果State是动态分配的对象,可以用std::shared_ptr或std::weak_ptr来管理它的生命周期,从根源上避免引用悬空问题。
修改Foo和Bar的代码示例:
#include <memory> class Foo { public: // 返回shared_ptr而非引用 std::shared_ptr<const State> get_state() const { return m_state; } void handle_event() { // 失效旧State,创建新的State实例 m_state = std::make_shared<const State>(/* 初始化参数 */); } private: std::shared_ptr<const State> m_state; }; class Bar { public: Bar(const Foo& foo) : m_foo(foo) {} void use_state() { // 获取最新的shared_ptr,只要持有它,State就不会被销毁 auto current_state = m_foo.get_state(); // 使用current_state->xxx访问状态成员 // ...业务逻辑... } private: const Foo& m_foo; };
这个方案的优点是借助智能指针的自动生命周期管理,彻底消除了悬空引用的风险;如果Bar需要长期持有状态,也可以结合观察者模式,在状态更新时获取新的shared_ptr。
内容的提问来源于stack exchange,提问作者Alex

