You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
};

关键细节解释

  1. 可变参数模板:template<typename handlerType, template<typename> class... mapTypes> 中的...表示接受任意数量符合template<typename>签名的模板类型参数,完美适配你定义的map::event_class1、map::event_class2这类映射结构。
  2. 参数包展开:我用了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...>();
    }
    
  3. 内存安全:给base类添加了虚析构函数,确保子类对象能被正确销毁;同时在管理器的析构函数中遍历数组释放所有动态分配的对象,彻底避免内存泄漏。另外禁用了拷贝移动操作,防止对象复制后出现指针悬空问题。
  4. 类型安全:每个映射类型都明确指定了对应的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:07:47