如何将类导出至DLL,避免名称修饰(Mangled)?
解决C++类导出时的名称修饰问题
嗨,我来帮你理清这个问题——首先得明确:extern "C"没法直接用来导出C++类,毕竟C语言根本不支持类这种东西,所以这种方法对类完全无效。要让导出的C++类避开烦人的名称修饰(或者说获得稳定的导出符号),主要有两种靠谱的方案:
方案一:用模块定义(.def)文件
这是最直接的方式,你可以手动在def文件里指定类要导出的符号,完全绕开编译器的名称修饰机制。
具体步骤:
- 新建一个后缀为
.def的文本文件,比如命名为PointDll.def,内容格式如下:
LIBRARY PointDll EXPORTS ??4Point@@QAEAAV0@ABV0@@Z @1 // 拷贝赋值运算符的修饰符号 ??4Point@@QAEAAV0@$$QAV0@@Z @2 // 移动赋值运算符的修饰符号 ?output@Point@@QAEXHH@Z @3 // output成员函数的修饰符号
这里的符号就是你用dumpbin.exe查到的那些长串,直接把它们列在EXPORTS下面就行,后面的@数字是导出序号,随便设,只要不重复就OK。
- 把这个def文件添加到你的VS项目里,或者在项目属性的「链接器 -> 输入 -> 模块定义文件」中指定它的路径。
这么操作之后,编译出来的DLL导出表就会用你在def文件里指定的符号,只要类的接口不变,这些符号就不会变,完全不用担心编译器乱改。
方案二:用__declspec(dllexport)配合统一导出宏
这种方式是靠编译器指令标记要导出的类,虽然最终还是会有名称修饰,但只要用相同的编译器版本和编译选项,符号就是稳定的,而且比def文件好维护(不用手动复制那些长长的修饰符号)。
实现步骤:
- 先定义一个导出宏,根据当前是编译DLL还是使用DLL自动切换
dllexport和dllimport:
#ifdef POINTDLL_EXPORTS #define POINTDLL_API __declspec(dllexport) #else #define POINTDLL_API __declspec(dllimport) #endif
(注意:POINTDLL_EXPORTS这个宏要在你的DLL项目里定义,比如去项目属性的「C/C++ -> 预处理器 -> 预处理器定义」里加上它)
- 用这个宏标记你的
Point类:
class POINTDLL_API Point { public: void output(int a, int b); Point& operator=(const Point& other); Point& operator=(Point&& other); // 其他成员变量和函数 };
编译DLL的时候,编译器会自动导出这个类所有公共成员函数的符号。虽然这些符号还是被修饰过的,但只要你在调用DLL的项目里用同样的编译器、同样的编译选项(比如相同VS版本、同是Debug/Release模式、调用约定一致),就能正常链接使用这个类。
额外提醒
- 如果你的DLL需要给不同编译器写的程序调用(比如VS和GCC混着用),那def文件是更稳妥的选择,因为不同编译器的名称修饰规则不一样,
__declspec(dllexport)导出的符号跨编译器会不兼容。 - 你看到的那些奇怪的长串符号,其实是C的「名称修饰(name mangling)」,用来区分重载函数、成员函数这些,编译器靠它实现C的重载、多态这些特性。
内容的提问来源于stack exchange,提问作者Liu Guangxuan
相关产品推荐
相关产品推荐

