C++11可变参数事件类管理器实现问题求助
解决可变参数版本的事件类管理器问题
我明白你想要把当前只能处理单个事件类的event_class_mgr扩展成可以接受多个事件类映射类型的版本,这样就能一次性初始化所有对应的事件类实例并放到std::array的对应位置。下面是具体的实现方案:
核心修改点
- 将
event_class_mgr改为可变参数模板,支持传入多个map::event_classX这样的模板类型 - 实现参数包展开逻辑,逐个初始化每个事件类并放入数组
- 补充内存安全相关的处理,避免泄漏和指针悬空问题
修改后的完整代码
#include <array> class handler { public: void handle() {} }; enum event_class_types { eclass1 = 0, eclass2, num_classes, }; class base { public: // 基类必须添加虚析构,否则删除子类对象时会有未定义行为 virtual ~base() = default; }; template<typename handlerType> class event_class1 : public base { public: event_class1(handlerType& handler) : handler_(handler) {} private: handlerType& handler_; }; template<typename handlerType> class event_class2 : public base { public: event_class2(handlerType& handler) : handler_(handler) {} private: handlerType& handler_; }; namespace map { template<typename handlerType> struct event_class1 { using type = ::event_class1<handlerType>; static constexpr int id = eclass1; }; template<typename handlerType> struct event_class2 { using type = ::event_class2<handlerType>; static constexpr int id = eclass2; }; } // 可变参数版本的事件类管理器 template<typename handlerType, template<typename> class... mapTypes> class event_class_mgr { public: event_class_mgr(handlerType& handler) : handler_(handler) { // 展开所有传入的映射类型,逐个初始化事件类 add_classes<mapTypes...>(); } // 析构函数:释放所有动态分配的对象,避免内存泄漏 ~event_class_mgr() { for (auto ptr : my_classes_) { delete ptr; } } // 禁用拷贝和移动操作,防止指针悬空 event_class_mgr(const event_class_mgr&) = delete; event_class_mgr& operator=(const event_class_mgr&) = delete; event_class_mgr(event_class_mgr&&) = delete; event_class_mgr& operator=(event_class_mgr&&) = delete; private: // C++17折叠表达式版本:简洁展开参数包(推荐) template<template<typename> class... MapTypes> void add_classes() { (add_single_class<MapTypes>(), ...); } // 初始化单个事件类并放入数组的对应位置 template<template<typename> class MapType> void add_single_class() { using EventClass = typename MapType<handlerType>::type; my_classes_[MapType<handlerType>::id] = new EventClass(handler_); } handlerType& handler_; std::array<base*, event_class_types::num_classes> my_classes_{}; }; int main(int argc, char** argv) { handler my_handler; // 现在可以直接传入多个映射类型了! event_class_mgr<handler, map::event_class1, map::event_class2> cm(my_handler); return 0; };
关键细节解释
- 可变参数模板:
template<typename handlerType, template<typename> class... mapTypes>中的...表示接受任意数量符合template<typename>签名的模板类型参数,完美适配你定义的map::event_class1、map::event_class2这类映射结构。 - 参数包展开:我用了C17的折叠表达式
(add_single_class<MapTypes>(), ...),它会自动遍历所有传入的映射类型,逐个调用初始化函数。如果你的编译器不支持C17,可以用递归方式替代:// 递归终止函数:参数包为空时调用 void add_classes() {} // 递归处理参数包:先处理第一个类型,再递归处理剩余的 template<template<typename> class CurrentMap, template<typename> class... RestMaps> void add_classes() { add_single_class<CurrentMap>(); add_classes<RestMaps...>(); } - 内存安全:给
base类添加了虚析构函数,确保子类对象能被正确销毁;同时在管理器的析构函数中遍历数组释放所有动态分配的对象,彻底避免内存泄漏。另外禁用了拷贝移动操作,防止对象复制后出现指针悬空问题。 - 类型安全:每个映射类型都明确指定了对应的
id和事件类类型,确保实例被放到数组的正确位置,不会出现类型不匹配的问题。
关于传递枚举的替代方案
如果你想直接传递枚举值而非映射类型,也可以实现另一个版本的管理器,但需要额外维护枚举到事件类的映射:
// 定义枚举到事件类的映射模板 template<event_class_types E> struct enum_to_event_class; template<> struct enum_to_event_class<eclass1> { template<typename handlerType> using type = event_class1<handlerType>; }; template<> struct enum_to_event_class<eclass2> { template<typename handlerType> using type = event_class2<handlerType>; }; // 接受枚举参数的管理器 template<typename handlerType, event_class_types... Es> class event_class_mgr_enum { public: event_class_mgr_enum(handlerType& handler) : handler_(handler) { (add_single_enum<Es>(), ...); } private: template<event_class_types E> void add_single_enum() { using EventClass = typename enum_to_event_class<E>::template type<handlerType>; my_classes_[E] = new EventClass(handler_); } handlerType& handler_; std::array<base*, num_classes> my_classes_{}; }; // 使用方式 event_class_mgr_enum<handler, eclass1, eclass2> cm_enum(my_handler);
不过这种方式需要额外维护映射关系,灵活性不如直接传递映射类型的方案。
内容的提问来源于stack exchange,提问作者mmodel1
相关产品推荐
相关产品推荐

