在 C++ 中,__declspec(dllexport)
和 __declspec(dllimport)
是用于标记 动态链接库(DLL) 中符号导出和导入的关键字。它们是 Microsoft 编译器(如 Visual Studio)专有的修饰符,用于控制符号在动态链接库中的可见性。
以下是详细介绍:
1. __declspec(dllexport)
__declspec(dllexport)
告诉编译器将函数或变量导出(Export)到动态链接库(DLL),以便其他模块(如可执行文件或其他 DLL)可以使用这些符号。
使用场景
- 当您创建一个动态链接库(DLL)时,需要使用
__declspec(dllexport)
导出希望公开的函数、变量或类。
示例
导出函数
1 2
| __declspec(dllexport) void myFunction();
|
1 2 3 4 5 6
| #include "MyLibrary.h"
__declspec(dllexport) void myFunction() { }
|
导出变量
1 2
| __declspec(dllexport) int myGlobalVar;
|
1 2 3 4
| #include "MyLibrary.h"
__declspec(dllexport) int myGlobalVar = 42;
|
导出类
1 2 3 4 5
| class __declspec(dllexport) MyClass { public: void myMethod(); };
|
1 2 3 4 5 6
| #include "MyLibrary.h"
void MyClass::myMethod() { }
|
2. __declspec(dllimport)
__declspec(dllimport)
告诉编译器从动态链接库(DLL)中导入(Import)符号,以便在当前模块中使用。
使用场景
- 当您使用一个动态链接库(DLL)中的函数、变量或类时,需要使用
__declspec(dllimport)
来声明这些符号。
示例
假设您已经创建了一个动态链接库,并导出了一个函数或变量:
导入函数
1 2
| __declspec(dllimport) void myFunction();
|
1 2 3 4 5 6 7
| #include "MyLibrary.h"
int main() { myFunction(); return 0; }
|
导入变量
1 2
| __declspec(dllimport) int myGlobalVar;
|
1 2 3 4 5 6 7
| #include "MyLibrary.h"
int main() { int value = myGlobalVar; return 0; }
|
导入类
1 2 3 4 5
| class __declspec(dllimport) MyClass { public: void myMethod(); };
|
1 2 3 4 5 6 7 8
| #include "MyLibrary.h"
int main() { MyClass obj; obj.myMethod(); return 0; }
|
3. 动态切换 __declspec(dllexport)
和 __declspec(dllimport)
在同一个头文件中,通常需要根据编译目标(DLL 还是客户端)动态切换 __declspec(dllexport)
和 __declspec(dllimport)
。
实现方法
通过定义预处理器宏来控制导出和导入。例如:
1 2 3 4 5 6 7 8
| #ifdef MYLIBRARY_EXPORTS #define MYLIBRARY_API __declspec(dllexport) #else #define MYLIBRARY_API __declspec(dllimport) #endif
MYLIBRARY_API void myFunction();
|
在编译 DLL 时定义 MYLIBRARY_EXPORTS
,而在使用 DLL 时不定义 MYLIBRARY_EXPORTS
。
编译动态库(DLL)
在生成 DLL 时,通过编译器选项定义 MYLIBRARY_EXPORTS
:
1
| cl /D MYLIBRARY_EXPORTS MyLibrary.cpp /LD
|
使用动态库
在使用 DLL 的项目中,不需要定义 MYLIBRARY_EXPORTS
,编译器会自动将其视为 __declspec(dllimport)
。
4. 注意事项
4.1 不使用 __declspec
的情况
- 如果不使用
__declspec(dllexport)
导出符号,编译器不会将符号公开到 DLL 导出表中,外部模块无法直接访问它们。
- 如果不使用
__declspec(dllimport)
导入符号,编译器会假定符号是静态链接的,但可能会导致运行时错误。
4.2 使用 DEF 文件导出符号
除了使用 __declspec(dllexport)
,还可以通过模块定义文件(.def
文件)导出符号。这在需要大量导出符号时更加方便:
示例 DEF 文件:
1 2 3 4
| LIBRARY MyLibrary EXPORTS myFunction myGlobalVar
|
生成 DLL:
1
| cl MyLibrary.cpp /LD /DEF:MyLibrary.def
|
4.3 导出 C 函数(避免 C++ 名字修饰)
如果导出的函数是用 C++ 编写的,默认情况下会启用名字修饰(name mangling)。为了避免名字修饰,可以使用 extern "C"
:
1
| extern "C" __declspec(dllexport) void myFunction();
|
5. 总结
- **
__declspec(dllexport)
**:用于将符号导出到 DLL,供其他模块使用。
- **
__declspec(dllimport)
**:用于从 DLL 导入符号到当前模块。
- 动态切换导出/导入:使用预处理器宏(如
MYLIBRARY_API
)动态控制。
- DEF 文件:在需要导出大量符号时可以替代
__declspec(dllexport)
。
如有具体问题或代码场景需要分析,可以进一步探讨! 😊