C++热更新最可行路径是std::filesystem+dlopen/LoadLibrary,但仅支持函数逻辑替换,不支持类结构、全局变量或模板实例变更;需严格保证ABI一致、extern "C"导出、原子函数指针切换及跨DLL内存隔离。

热更新在 C++ 里不是语言特性,得自己搭链路
直接说结论:std::filesystem + dlopen/LoadLibrary 是目前最可行的路径,但必须接受「只能热替换函数逻辑,不能热改类结构、全局变量或模板实例」。C++ 没有运行时类型系统支撑,所有热更新本质都是“用新动态库覆盖旧函数指针”,一旦 ABI 不一致(比如加了虚函数、改了成员顺序),segfault 或静默错值是大概率事件。
Linux 下用 dlopen + dlsym 加载函数指针
核心思路是把可热更代码编译成独立 .so 文件,主程序通过符号名查函数地址并调用。关键不是“怎么加载”,而是“怎么安全切换”:
- 每次热更前先调用
dlclose,再dlopen新版本 —— 但注意:如果旧.so里还有线程在执行,dlclose不会立即卸载,得靠引用计数归零才真正释放 - 函数签名必须完全一致,包括调用约定(
extern "C"必须加,否则 C++ name mangling 导致dlsym找不到) - 不要在热更期间调用正在被替换的函数;建议用原子指针(
std::atomic<void></void>)存函数地址,更新时先写新地址,再等旧调用自然退出
extern "C" int compute(int a, int b) { return a + b; }
Windows 下 LoadLibrary 需绕过模块句柄冲突
和 Linux 类似,但 Windows 的 HMODULE 在 DLL 卸载后不保证资源立刻释放,尤其涉及 CRT 初始化或静态局部变量时容易崩。常见错误现象是第二次 LoadLibrary 返回 NULL,GetLastError() 报 ERROR_DLL_NOT_FOUND 或 ERROR_INVALID_HANDLE:
- 确保 DLL 编译时禁用 CRT 静态链接(用
/MD而非/MT),避免多个 CRT 实例打架 - 不要在 DLL 的
DllMain里做任何可能阻塞或调用其他 DLL 的事(比如printf、new) - 用
FreeLibraryAndExitThread替代裸FreeLibrary,尤其当 DLL 内有线程在跑时
热更新失败时最常踩的三个坑
不是编译不过,而是运行时悄无声息地坏掉:
立即学习“C++免费学习笔记(深入)”;
-
std::string或std::vector跨 DLL 边界传递 —— 两边 STL 实现或分配器不一致,导致内存越界或 double-free - 热更后没重置单例或静态状态,新 DLL 的构造函数没触发,旧状态还在用老地址
- 调试器附加状态下
dlopen/LoadLibrary失败(GDB/VS 默认拦截动态加载),得关掉 “Enable native code debugging” 或用set follow-fork-mode child
真正难的从来不是“怎么加载新代码”,而是“怎么让旧状态和新代码和平共处”。哪怕只改一行 return 42;,只要它依赖某个全局 std::map 的迭代器,就可能卡死在析构阶段。











