如何修复/重设计C++ Event类体系以避免要求Manager成员全部公开?
解决方案
首先明确:绝对不要直接将Manager的所有数据设为公有,这会彻底破坏封装性,后续维护、线程安全保障、内部逻辑迭代都会遇到不可控的问题。以下是两种适配你场景的高扩展性方案:
方案1:最小改动适配现有继承架构
你不需要依赖C++不存在的友元继承特性,只要利用现有基类做访问代理即可,整体结构几乎不用改:
- 仅将Event基类设为Manager的友元
- 在Event基类中实现
protected权限的访问/操作接口,封装对Manager私有成员的操作 - 所有派生Event类通过基类的受保护接口访问Manager数据,不需要单独加友元
代码示例
class Event; class Manager { friend class Event; // 仅基类是友元,不需要给每个派生类加 public: Manager() {} // 队列插入、处理逻辑省略 private: int dataInt; double dataReal; std::string dataStr; std::queue<Event *> events; // 其他私有成员保持私有 }; class Event { public: Event(Manager * m) : manager(m) { process = std::bind(&Event::processKernel, this); } std::function<void(void)> process; protected: virtual void processKernel() = 0; // 派生类可调用的受保护访问接口,封装Manager私有成员操作 int getManagerInt() const { return manager->dataInt; } void setManagerInt(int val) { manager->dataInt = val; } double getManagerReal() const { return manager->dataReal; } void setManagerReal(double val) { manager->dataReal = val; } // 后续新增事件需要新操作时,只需要在这里加对应的接口即可 private: Manager * manager; }; // 派生类不需要任何改动,直接用基类接口即可 class SpecificEvent: public Event { public: SpecificEvent(Manager * m, int p) : Event(m), param(p) { } void processKernel() { setManagerInt(param); // 调用基类接口,不需要直接访问Manager私有成员 } private: int param; };
方案2:无继承轻量事件队列(更推荐)
如果你不需要强制的Event继承体系,用C++11及以上标准支持的std::function实现无类事件队列,扩展性更强,完全避免派生类新增成本:
- Manager的事件队列直接存储
std::function<void(Manager&)>类型的可调用对象 - Manager只对外暴露封装好的操作接口,所有内部数据保持私有
- 提交事件时直接用lambda捕获参数、写业务逻辑,不需要新增任何派生类
代码示例
#include <functional> #include <queue> #include <mutex> class Manager { public: // 提交事件接口 void pushEvent(std::function<void(Manager&)> event) { std::lock_guard<std::mutex> lock(queueMtx); events.push(std::move(event)); } // 事件处理循环 void processLoop() { while(running) { std::function<void(Manager&)> curEvent; { std::lock_guard<std::mutex> lock(queueMtx); if(events.empty()) continue; curEvent = std::move(events.front()); events.pop(); } curEvent(*this); // 执行事件逻辑 } } // 对外暴露的封装操作接口,线程安全可在这里统一处理 void setInt(int val) { dataInt = val; } int getInt() const { return dataInt; } void setReal(double val) { dataReal = val; } // 新增操作只需要加对应的成员函数,不需要修改事件处理逻辑 private: bool running = true; std::mutex queueMtx; std::queue<std::function<void(Manager&)>> events; int dataInt; double dataReal; std::string dataStr; // 所有内部数据保持私有 }; // 使用示例 int main() { Manager manager; int param = 10; // 提交事件,不需要定义任何派生类 manager.pushEvent([param](Manager& m) { m.setInt(param); }); double realParam = 3.14; manager.pushEvent([realParam](Manager& m) { m.setReal(realParam); }); return 0; }
这个方案完全避免了事件类的继承体系维护,新增任意类型的事件都不需要修改Manager代码,也不需要新增类,是现在C++事件系统的主流实现方式。
内容的提问来源于stack exchange,提问作者turanc
相关产品推荐
相关产品推荐

