不能直接导出C++类,必须通过extern "C"工厂函数返回纯虚接口指针,并用-fvisibility=hidden(Linux)或__declspec(dllexport)(Windows)控制符号可见性,确保new/delete同空间、调用约定一致、头文件无编译器扩展。

共享库中不能直接导出类定义
Windows 的 __declspec(dllexport) 和 Linux 的隐藏符号默认行为,都不支持直接将 C++ 类(含虚表、RTTI、构造/析构逻辑)安全地跨 DSO 边界导出。客户端链接时可能因 ABI 不兼容、内存布局差异或 vtable 偏移错乱而崩溃——哪怕用同一编译器同一版本,只要编译选项稍有不同(如 -fPIC 与非 -fPIC 混用),就极大概率出问题。
真正可行的路径是:只导出纯 C 风格函数接口,把类实例的生命周期和操作完全封装在共享库内部。
必须用工厂函数 + 抽象基类指针
对外暴露一个纯虚接口(头文件中定义),所有成员函数为 public virtual,析构函数为 virtual;实现类放在共享库源码里,不暴露具体类型;通过导出的 C 函数返回指向该接口的 new 出来的指针。
-
MyInterface.h必须不含任何平台/编译器私有扩展,且客户端和服务端包含同一份头文件 - 导出函数必须是
extern "C",避免 name mangling,例如:extern "C" MyInterface* create_instance(); - 对应释放函数也必须导出:
extern "C" void destroy_instance(MyInterface* p);,因为delete必须与new在同一地址空间执行(否则可能触发 malloc/free 不匹配)
Linux 下需显式控制符号可见性
默认 GCC 会导出所有全局符号,但你不希望实现类名、辅助函数、静态变量被外部看到。必须加编译参数 -fvisibility=hidden,并在接口类声明和导出函数上显式标记 __attribute__((visibility("default")))。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
否则可能出现:客户端能调用 create_instance(),但一调用其返回对象的虚函数就 segfault——因为虚表里某个函数地址解析到了客户端未定义的符号,或链接到了错误版本的弱符号。
示例关键片段:
class __attribute__((visibility("default"))) MyInterface {
public:
virtual ~MyInterface() = default;
virtual int do_work() = 0;
};
<p>extern "C" <strong>attribute</strong>((visibility("default"))) MyInterface<em> create_instance();
extern "C" <strong>attribute</strong>((visibility("default"))) void destroy_instance(MyInterface</em>);
Windows DLL 中导出函数要小心 __cdecl 调用约定
MSVC 默认 C++ 成员函数用 __thiscall,但 C 接口必须统一为 __cdecl(否则客户端用 MinGW 或不同 MSVC 版本调用时栈不平衡)。显式声明调用约定是必须的:
extern "C" __declspec(dllexport) MyInterface* __cdecl create_instance();extern "C" __declspec(dllexport) void __cdecl destroy_instance(MyInterface*);
同时确保客户端链接时未定义 _CRT_SECURE_NO_WARNINGS 等影响 CRT 行为的宏——它们可能导致 malloc/free 与 DLL 内部 new/delete 不匹配,进而破坏 destroy_instance 的安全性。
真正的难点从来不是“怎么写出来”,而是确保客户端与共享库之间没有隐式的内存管理、异常传播、STL 对象(如 std::string)或 RTTI 交叉传递——这些一旦发生,调试时连崩溃点都难以定位。

















