c++oding="utf-8" ?>
__declspec(dllexport)是windows专属扩展,linux的gcc/clang不识别,跨平台必须用宏封装条件编译:windows下展开为__declspec(dllexport/dllimport),linux/macos下展开为__attribute__((visibility("default"))),并配合-fvisibility=hidden确保符号可控导出。

为什么 __declspec(dllexport) 在 Linux 上根本不存在
因为它是 Microsoft Visual C++ 的 Windows 专属扩展,GCC/Clang 完全不识别。直接在跨平台代码里写 __declspec(dllexport) 会导致 Linux 编译失败,错误类似:error: expected identifier before '__declspec'。跨平台动态库导出必须用编译器无关的机制统一控制符号可见性。
实际做法是:用宏封装导出声明,按编译器和平台条件展开:
#ifdef _WIN32
#ifdef BUILDING_MYLIB
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
#else
#define MYLIB_API __attribute__((visibility("default")))
#endif
注意两点:BUILDING_MYLIB 需在构建动态库时定义(如 CMake 中加 add_compile_definitions(BUILDING_MYLIB)),而 __attribute__((visibility("default"))) 是 GCC/Clang 的标准方式,但默认行为是 visibility("hidden"),必须显式设为 default 才能导出。
Linux 下 gcc -fvisibility=hidden 不生效的常见原因
即使加了这个编译选项,函数仍可能意外导出——根源在于头文件中没正确标注 MYLIB_API。C++ 的符号导出粒度是函数/类级别,不是整个 so 文件;未标记的符号哪怕在共享库内定义,也会被 -fvisibility=hidden 自动隐藏,但一旦漏标,又没其他保护,就可能因模板实例化、内联或弱符号等机制暴露出去。
- 类成员函数必须每个都加
MYLIB_API,不能只加在类声明上(除非用class MYLIB_API MyClass { ... };这种语法,但兼容性差) - 模板函数不能直接导出(链接时无实体),需显式实例化并标注,例如:
template MYLIB_API void my_template_func<int>();</int> - 全局变量同理,需用
MYLIB_API extern int my_var;声明 + 定义处也加宏
如何验证符号是否真的被导出(而不是只是“看起来有”)
别信 nm -C libmylib.so | grep MyFunc 的输出——它显示的是所有符号,包括本地符号(T 或 t)和未裁剪的调试符号。真正被外部可链接的符号必须满足两个条件:可见性为 default,且类型是 T(全局文本)或 D(全局数据)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
可靠检查命令是:
objdump -T libmylib.so | grep MyFunc
或者更彻底地过滤掉本地符号:
readelf -Ws libmylib.so | awk '$8 ~ /FUNC|OBJECT/ && $3 == "DEFAULT" {print}'
如果结果为空,说明该符号没导出成功。常见干扰项:nm 显示的 U(undefined)、W(weak)、t(local text)都不算有效导出符号。
C++ 类型信息与 RTTI 导致的跨平台 ABI 冲突
即使函数符号导出正常,C++ 动态库在跨平台或跨编译器调用时仍可能崩溃,典型现象是 std::bad_cast、虚函数调用跳转到错误地址,或 typeid 比较失败。这是因为 GCC 和 Clang 对 typeinfo 名称的编码规则不同,且 RTTI 数据结构本身不保证 ABI 稳定。
解决路径很窄:
- 避免通过动态库接口传递非 POD 类型(尤其是含虚函数、模板、STL 容器的类)
- 导出纯 C 接口(
extern "C")是最稳妥的,所有 C++ 实现细节封装在内部 - 若必须导出 C++ 类,强制关闭 RTTI:
-fno-rtti(但会禁用dynamic_cast和异常类型捕获) - 确保所有平台使用相同 STL 实现(如全部用 libc++ 或全部用 libstdc++),否则
std::string内存布局都可能不兼容
最常被忽略的一点:CMake 中 set(CMAKE_CXX_STANDARD_REQUIRED ON) 必须显式设置,否则不同平台可能默认用 C++11/C++17,导致 std::optional 等类型 ABI 不一致。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










