C++插件系统能跑起来的关键在于接口定义稳定、工厂函数可寻址、对象生命周期由框架统一控制;接口须为纯虚类且无STL类型,工厂函数用extern "C"导出,创建与销毁必须成对由框架管理。

dlsym 返回 nullptr、虚函数调用崩溃、或内存泄漏。
接口类必须是纯抽象且ABI稳定的
很多初学者把 IPlugin 写成带成员变量或非虚析构的类,结果在主程序和插件中因 vtable 偏移不一致而 crash。真正安全的做法是:
- 接口类只含纯虚函数 +
virtual ~IPlugin() = default;(不能有 inline 实现) - 禁止在接口中使用
std::string、std::vector等 STL 类型作为参数或返回值(不同编译器/标准库版本 ABI 不兼容) - 所有数据交换走 C 风格接口:用
const char*、int、void*和自定义 POD 结构体 - 头文件需用
#pragma pack(1)或alignas显式对齐,避免结构体填充差异
dlopen + dlsym 加载流程必须严格校验
Linux 下 dlopen 成功不代表插件可用,常见错误包括符号未导出、RTLD_LOCAL 导致符号不可见、路径拼接错误等。
- 加载时务必用
RTLD_LAZY | RTLD_GLOBAL:后者确保插件内部依赖的符号能从主程序解析 - 工厂函数名必须用
extern "C"修饰,否则 C++ name mangling 会让dlsym找不到create_plugin - 每次
dlsym后必须检查返回值是否为nullptr,并用dlerror()获取具体错误信息 - 插件路径建议用绝对路径,相对路径容易因
chdir或工作目录变化失效
插件对象的创建与销毁必须成对由框架控制
插件 DLL 中 new 出来的对象,不能由主程序直接 delete —— 跨模块堆分配/释放极大概率触发 double-free 或 heap corruption。
- 工厂函数返回对象指针,同时必须提供配套的
destroy_plugin(IPlugin*)函数(同样extern "C") - 主程序调用插件后,必须通过插件自己的
destroy函数释放内存,而非delete - 若插件需持有资源(如文件句柄、线程),其
init()/shutdown()必须成对调用,且框架需保证 shutdown 在 dlclose 之前完成
Windows 下 LoadLibrary + GetProcAddress 的特殊处理
Windows 比 Linux 更敏感:DLL 依赖缺失、架构不匹配(x64 vs x86)、延迟加载失败都会静默失败。
立即学习“C++免费学习笔记(深入)”;
- 调用
LoadLibrary前先用SetErrorMode(SEM_FAILCRITICALERRORS)防止弹窗阻塞 -
GetProcAddress查找函数时,函数名必须与 DLL 导出表完全一致(注意 DEF 文件或__declspec(dllexport)是否生效) - 推荐用
GetModuleFileName+GetLastError辅助定位加载失败原因,而不是只看返回值 - 卸载前务必调用插件的清理函数,再调用
FreeLibrary;多次FreeLibrary同一模块句柄会引发未定义行为
destroy 调用,整个进程的堆状态就可能不可恢复。


















