C++11如何实现运行时选择模板类,无需手动维护字符串与类的映射?
解决方案(兼容C++11标准)
核心思路是利用静态变量自动初始化的特性,实现派生类的自动注册,不需要手动修改f函数的分支逻辑,新增派生类无需改动原有工厂代码。
实现步骤
1. 定义通用工厂注册表模板
我们先实现一个模板化的工厂结构,用来存储类名到构造函数的映射,适配不同基类和构造参数类型:
#include <string> #include <functional> #include <map> template<class Base, typename Arg> struct ClassFactory { // 全局注册表:key是类标识字符串,value是对应派生类的构造函数 static std::map<std::string, std::function<Base*(Arg&)>>& get_registry() { static std::map<std::string, std::function<Base*(Arg&)>> registry_instance; return registry_instance; } // 派生类注册工具 template<class Derived> static bool register_class(const std::string& class_label) { get_registry()[class_label] = [](Arg& arg) -> Base* { return new Derived(arg); }; return true; } };
2. 重写模板函数f
直接从注册表里查询对应的构造函数即可,不需要硬编码任何if-else分支:
template<class Base, typename T> Base* f(const std::string &type, T &c) { auto& registry = ClassFactory<Base, T>::get_registry(); auto iter = registry.find(type); if (iter != registry.end()) { return iter->second(c); } return nullptr; }
3. 派生类自动注册
仅需要在每个派生类的实现中添加少量注册代码,不需要修改其他业务逻辑:
// 示例派生类ClassA,和原有结构完全兼容 class ClassA: public BaseClass { public: static const std::string label; ClassA(YourParamType& c) { // 原有构造逻辑 } // 其他类成员... private: // 注册标记,静态变量初始化时自动完成注册 static const bool _registered_flag; }; // 类外初始化静态成员完成注册 const std::string ClassA::label = "ClassA"; const bool ClassA::_registered_flag = ClassFactory<BaseClass, YourParamType>::register_class<ClassA>(ClassA::label);
如果需要简化注册代码,可以封装一个统一宏:
#define AUTO_REGISTER_CLASS(BaseType, DerivedType, ArgType) \ const bool DerivedType::_registered_flag = ClassFactory<BaseType, ArgType>::register_class<DerivedType>(DerivedType::label)
之后每个派生类的注册仅需要一行代码:
AUTO_REGISTER_CLASS(BaseClass, ClassA, YourParamType);
注意事项
- 如果派生类需要支持多种构造参数类型,只需要给每种参数类型单独注册一次即可
- 注册表是函数内静态变量,第一次调用时才初始化,不会出现静态变量初始化顺序冲突问题
- 如果需要避免裸指针内存泄漏,可以把返回值改为
std::unique_ptr<Base>,仅需要修改工厂lambda的返回逻辑即可 - 原有函数调用方式完全不变,
BaseClass *b = f<BaseClass>("ClassA", DifferentObject);可以正常执行
内容的提问来源于stack exchange,提问作者ad1v7
相关产品推荐
相关产品推荐

