C++运行时添加用户自定义派生类对象的实现方案问询
运行时实例化自定义派生类的可行方案
问题背景
现有基类Foo:
class Foo{ virtual void init(); virtual void update(float dt); };
用户自定义派生类Bar:
class Bar : public Foo{ void init() override; void update(float dt) override; };
希望通过配置文件(格式如[Foo] : "Bar")在运行时查询Foo的派生类并实例化Bar对象,此前尝试基于type_info的工厂模式但无法通过type_info创建对象,且已知C++无原生反射,不想使用DLL,实际需求是为基础游戏引擎添加脚本系统。
方案1:手动注册的工厂模式(最常用)
核心是维护类名到创建函数的映射表,所有派生类在编译时主动注册到表中,运行时通过配置类名查表创建对象。
实现步骤
- 定义工厂类,包含静态映射表与核心方法:
#include <unordered_map> #include <string> #include <memory> class FooFactory { private: using CreateFunc = std::unique_ptr<Foo>(*)(); static std::unordered_map<std::string, CreateFunc> creators; public: static void registerCreator(const std::string& className, CreateFunc func) { creators[className] = func; } static std::unique_ptr<Foo> create(const std::string& className) { auto it = creators.find(className); return it != creators.end() ? it->second() : nullptr; } }; // 初始化静态映射表 std::unordered_map<std::string, FooFactory::CreateFunc> FooFactory::creators;
- 派生类添加自动注册逻辑:
class Bar : public Foo{ void init() override {} void update(float dt) override {} struct Register { Register() { FooFactory::registerCreator("Bar", []() { return std::make_unique<Bar>(); }); } }; static Register registerInstance; }; // 触发静态注册 Bar::Register Bar::registerInstance;
- 运行时解析配置并创建对象:
// 假设解析配置得到类名"Bar" std::unique_ptr<Foo> obj = FooFactory::create("Bar"); if (obj) { obj->init(); // 后续调用obj->update(dt) }
方案2:预处理器宏简化注册(减少重复代码)
如果派生类数量多,用宏定义一键完成注册:
#define REGISTER_FOO_DERIVED(className) \ struct className##Register { \ className##Register() { \ FooFactory::registerCreator(#className, []() { \ return std::make_unique<className>(); \ }); \ } \ }; \ static className##Register className##_register_instance;
派生类中只需一行代码:
class Bar : public Foo{ void init() override {} void update(float dt) override {} }; REGISTER_FOO_DERIVED(Bar);
方案3:嵌入脚本语言(适配游戏引擎脚本系统需求)
既然是为引擎做脚本系统,直接嵌入轻量脚本(如Lua)是更贴合需求的方案:
- 把
Foo的接口绑定到脚本,让用户在脚本中编写派生类逻辑; - 配置文件指定脚本路径,引擎加载脚本后,通过脚本API创建对象并绑定到C++层;
- 这种方式无需修改C++代码即可扩展功能,完全符合脚本系统的设计目标。
注意事项
- 所有派生类需和引擎代码一起编译(因为不用DLL),新增类后需重新编译;
- 若需热更新,脚本方案是最优选择;
- 多线程环境下,工厂类的注册、创建方法需加锁保证线程安全。
内容的提问来源于stack exchange,提问作者Spandan Roy
相关产品推荐
相关产品推荐

