如何在STL容器中存储带有抽象模板类型的抽象模板类?
解决CQRS命令处理器中STL容器存储抽象模板类的问题
嘿,我来帮你搞定这个CQRS命令处理器的存储问题!你现在的核心困境是STL容器没法直接存带抽象模板类型的类——毕竟抽象类不能实例化,而且不同模板参数的类类型完全不同,没法直接放进同一个容器。不过咱们可以通过统一抽象基类+模板适配器的方式来解决,下面一步步来拆解:
步骤1:完善命令抽象基类
你已经定义了ICommand作为所有命令的基类,这步非常关键,确保所有命令都能通过这个基类来统一传递。这里补全你没写完的SampleCommand代码,保证结构完整:
#include <memory> #include <vector> #include <unordered_map> #include <typeindex> #include <stdexcept> #include <iostream> struct ICommand { virtual ~ICommand() = default; }; struct SampleCommand : ICommand { int sampleParameter; SampleCommand() : sampleParameter(0) { } explicit SampleCommand(int sampleParameter) : sampleParameter(sampleParameter) { } };
步骤2:定义命令处理器的统一抽象接口
要把不同的命令处理器放进STL容器,首先得给它们一个共同的非模板基类。咱们定义ICommandHandler,它只提供一个处理命令的纯虚方法:
struct ICommandHandler { virtual ~ICommandHandler() = default; // 用unique_ptr管理命令的生命周期,避免内存泄漏 virtual void handle(std::unique_ptr<ICommand> command) = 0; };
步骤3:实现模板化的命令处理器适配器
接下来写一个模板类CommandHandler,它继承自ICommandHandler,负责把通用的ICommand转换为具体的命令类型,然后交给子类实现具体逻辑:
template<typename TCommand> class CommandHandler : public ICommandHandler { public: void handle(std::unique_ptr<ICommand> command) override { // 用dynamic_cast做安全类型转换,需要编译器开启RTTI(默认都开) if (TCommand* cmdPtr = dynamic_cast<TCommand*>(command.get())) { // 转移所有权到具体命令的unique_ptr,避免双重释放 auto concreteCmd = std::unique_ptr<TCommand>(command.release()); // 调用具体的处理逻辑 handleSpecific(std::move(concreteCmd)); } else { throw std::invalid_argument("Command type mismatch for handler"); } } private: // 留给子类实现的具体命令处理方法 virtual void handleSpecific(std::unique_ptr<TCommand> command) = 0; };
步骤4:实现具体命令的处理器
现在针对SampleCommand写具体的处理器,只需要继承CommandHandler<SampleCommand>并实现handleSpecific即可:
class SampleCommandHandler : public CommandHandler<SampleCommand> { private: void handleSpecific(std::unique_ptr<SampleCommand> command) override { // 这里写你的CQRS命令处理逻辑,比如操作领域模型、持久化等 std::cout << "处理SampleCommand,参数值:" << command->sampleParameter << std::endl; } };
步骤5:在STL容器中存储并使用处理器
现在就可以用STL容器(比如vector或者unordered_map)来存储ICommandHandler的智能指针了。如果需要更高效的命令分发,推荐用unordered_map结合std::type_index来做类型映射:
class CommandDispatcher { private: // 用命令类型作为键,映射到对应的处理器 std::unordered_map<std::type_index, std::unique_ptr<ICommandHandler>> handlers; public: // 注册具体命令的处理器 template<typename TCommand> void registerHandler(std::unique_ptr<CommandHandler<TCommand>> handler) { handlers[std::type_index(typeid(TCommand))] = std::move(handler); } // 分发命令到对应的处理器 void dispatch(std::unique_ptr<ICommand> command) { auto it = handlers.find(std::type_index(typeid(*command))); if (it != handlers.end()) { it->second->handle(std::move(command)); } else { throw std::runtime_error("没有为该命令注册处理器"); } } }; // 测试代码 int main() { CommandDispatcher dispatcher; // 注册SampleCommand的处理器 dispatcher.registerHandler(std::make_unique<SampleCommandHandler>()); // 创建并分发命令 auto sampleCmd = std::make_unique<SampleCommand>(42); dispatcher.dispatch(std::move(sampleCmd)); return 0; }
关键注意点
- 内存管理:全程用
std::unique_ptr来管理命令和处理器的内存,避免手动new/delete导致的泄漏。 - 类型安全:用
dynamic_cast做类型转换,确保只有匹配的命令才会被处理器处理,类型不匹配时抛出异常。 - 性能优化:用
std::type_index做映射的分发器,比遍历容器查找处理器效率更高,适合命令类型较多的场景。
内容的提问来源于stack exchange,提问作者Just Mike
相关产品推荐
相关产品推荐

