插件接口必须基于主程序定义的纯虚类并用extern "C"导出create_plugin等函数,版本校验、内存管理、路径处理、热重载状态重置、符号可见性控制及ODR规避为关键要点。

插件接口必须用纯虚类 + 显式导出符号
动态库加载后,C++ 没有运行时类型系统,主程序根本不知道插件里有什么函数。靠 extern "C" 导出几个固定函数名(比如 create_plugin、destroy_plugin)是底线;但光这样不够——你得让插件提供一个统一的、带版本控制的接口基类。
常见错误是直接导出具体类实例,结果主程序和插件用了不同 STL 版本或编译器选项,std::string 成员一访问就崩溃;或者插件返回了栈上对象指针,主程序一用就野指针。
- 插件 DLL/SO 必须导出一个 C 风格函数:
extern "C" PluginInterface* create_plugin(int api_version) -
PluginInterface是主程序定义的纯虚类,所有虚函数末尾加= 0,不含成员变量、不继承 STL 容器 - 主程序检查
api_version是否在支持范围内,不匹配就拒绝加载,别试图“兼容” - 插件内部 new 出来的
PluginInterface实例,由主程序调用destroy_plugin统一 delete
Windows 下 LoadLibrary 失败?先查依赖和路径
不是代码写错了,90% 是 LoadLibrary 返回 NULL 却没调 GetLastError 看具体错误码。最常踩的坑是插件 DLL 依赖了主程序没带的运行时(比如插件用 /MDd 编译,主程序用 /MD),或者插件引用了另一个插件但路径不在 PATH 或当前目录。
- 用
Dependencies.exe(不是旧版 Dependency Walker)打开插件 DLL,看红色标出的缺失模块 - 加载前调用
SetDllDirectory(L"."),确保只从插件同目录搜依赖,避免系统目录干扰 - 不要用相对路径如
"plugins/my_mod.dll"直接传给LoadLibrary,先用GetFullPathName转成绝对路径 - 如果插件需要读取自己的资源文件,别假设工作目录是插件所在目录——用
GetModuleFileName获取 DLL 路径再拼接
插件热重载时,全局状态和单例必须重置
游戏引擎运行中卸载再重载插件,看似只是重新 FreeLibrary + LoadLibrary,但只要插件里用了静态变量、全局 std::map、或第三方库的单例(比如 spdlog::default_logger()),旧状态就会残留,新插件一初始化就冲突。
立即学习“C++免费学习笔记(深入)”;
- 插件接口里必须提供
shutdown()和initialize()两个虚函数,主程序在卸载前调shutdown(),加载后立即调initialize() - 禁止在插件 DLL 内部使用
static局部变量缓存数据;改用插件实例成员变量 - 如果用了第三方 SDK(如 PhysX、FMOD),必须确认其 API 支持多次 init/shutdown;否则只能整个进程重启
- 主程序要记录每个插件的加载时间戳,当检测到 DLL 文件被修改,强制触发 unload → reload 流程,而不是等下次启动
跨平台符号可见性:Linux/macOS 的 -fvisibility=hidden 不是可选项
Windows 默认隐藏 DLL 符号,但 Linux/Mac 的共享库默认导出所有符号。如果不加控制,插件里的辅助函数、模板实例、甚至内联函数都可能和主程序或其他插件冲突,引发诡异的 ODR(One Definition Rule)错误——比如两个插件都定义了同名的 detail::hash_combine,链接器随便选一个用。
- 编译插件时必须加
-fvisibility=hidden,并在导出函数/类前显式加__attribute__((visibility("default"))) - 头文件里用宏统一处理:
#define PLUGIN_API __attribute__((visibility("default"))),然后extern "C" PLUGIN_API PluginInterface* create_plugin(...) - macOS 还要额外加
-fvisibility-inlines-hidden,否则内联函数仍会导出 - 别信“用
nm -D看不到就是没导出”——有些符号即使没出现在动态符号表里,也可能通过 weak symbol 影响链接行为
真正难的不是第一次加载插件,而是三年后第 47 个插件上线时,它悄悄覆盖了第 12 个插件注册的事件回调,而日志里只有一行 pure virtual method called。










