不能直接用std::map存构造器,因为构造逻辑需状态隔离且C++无运行时类名反射,必须显式注册工厂函数;实操建议用std::unordered_map存储工厂函数,插件提供统一create方法,注册时检查重复并归一化名称,导出函数须用extern "C"防止符号丢失。

为什么不用 std::map<:string std::function>></:string> 直接存构造器?
因为构造逻辑往往需要状态隔离(比如插件需持有独立配置、日志句柄),单纯函数指针无法承载上下文。更关键的是,C++ 没有运行时类名反射,std::string 到类型信息的映射必须手动维护——你不能靠名字自动 new 出 MyPlugin,必须显式注册其构造行为。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
std::unordered_map<:string std::function>(const PluginConfig&) >></:string>存注册的工厂函数,而非裸类指针或静态实例 - 每个插件类提供静态
create方法,统一签名,避免 lambda 捕获导致的生命周期隐患 - 注册时机放在插件模块的
init()函数中(通常由主程序显式调用),不依赖全局对象构造顺序
如何防止插件重复注册或名称冲突?
常见错误现象:register_plugin("logger") 被调用两次,第二次静默覆盖,后续加载时行为突变却无提示。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 注册函数返回
bool,插入前用map.find(key) == map.end()检查,冲突时直接return false - 在调试构建中启用
assert:若发现已存在 key,触发断言并打印当前调用栈(可用__FILE__和__LINE__) - 插件名建议强制小写 + 下划线(如
"http_client"),避免大小写平台差异;注册前先std::transform归一化
BasePlugin 的析构必须是虚函数,但为什么还要禁止裸指针传递?
即使有虚析构,若用户代码中保存了 BasePlugin* 并在引擎卸载后访问,就是悬垂指针。反射引擎只负责创建和管理生命周期,不解决外部引用安全问题。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 引擎对外只暴露
std::shared_ptr<baseplugin></baseplugin>或std::unique_ptr<baseplugin></baseplugin>,绝不返回裸指针 - 提供
get_plugin(const std::string& name)接口,内部用weak_ptr缓存已创建实例,避免重复构造;同时支持按需销毁(remove_plugin清空对应weak_ptr) - 若插件需跨线程使用,确保
shared_ptr构造/拷贝在线程安全上下文中完成(std::make_shared本身线程安全,但注册表读写需加锁)
Windows DLL 和 Linux SO 加载后,如何保证符号不被优化掉?
常见错误现象:插件动态库编译后,主程序调用 dlsym(Linux)或 GetProcAddress(Windows)找不到 init_plugin 符号,返回 nullptr。
原因往往是编译器将未引用的全局函数整个优化掉了,或 C++ 名称修饰导致符号名失真。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 插件导出函数必须用
extern "C"声明,禁用名称修饰:extern "C" { __declspec(dllexport) void init_plugin(PluginRegistry& reg); } - Linux 下编译插件时加
-fvisibility=hidden,再对导出函数加__attribute__((visibility("default"))) - Windows 下确保 DLL 工程设置 “导出所有符号” 关闭,仅靠
__declspec(dllexport)显式导出,避免污染符号表
字符串映射本身很简单,难的是让每个环节都拒绝隐式假设——从名字归一化、工厂函数生命周期、到动态符号可见性,漏掉任何一环,反射就变成“反射失败”。


















