【C++】显示与隐式加载dll的使用与区别
一、什么是 DLL?
DLL(Dynamic Link Library) 是 Windows 下的动态链接库,包含可被多个程序共享的函数、资源或类。使用 DLL 可以实现代码复用、模块化设计和插件机制。
在 C++ 中,调用 DLL 中的函数有两种主要方式:
- 隐式链接(Implicit Linking)
- 显式链接(Explicit Linking)
二、隐式链接(Implicit Linking)
1. 原理
程序启动时自动加载 DLL,通过 .lib
导入库将 DLL 中的函数符号链接到可执行文件中。
2. 使用步骤
(1)准备三个文件:
MyDll.dll
:动态库文件MyDll.lib
:导入库(由 DLL 生成)MyDll.h
:声明导出函数
(2)头文件示例(MyDll.h)
#ifdef __cplusplus
extern "C" {
#endif__declspec(dllimport) int Add(int a, int b);#ifdef __cplusplus
}
#endif
注意:dllimport
表示从 DLL 导入函数。
(3)链接 .lib
文件
在项目中添加 .lib
路径,并链接:
#pragma comment(lib, "MyDll.lib")
(4)直接调用函数
#include "MyDll.h"int result = Add(3, 4); // 直接像普通函数一样调用
3. 特点
优点 | 缺点 |
---|---|
使用简单,像调用本地函数 | 启动时必须找到 DLL,否则程序无法启动 |
编译期检查函数签名 | 不支持动态选择或延迟加载 |
性能略高(无需查表) | 难以实现插件系统或热更新 |
三、显式链接(Explicit Linking)
1. 原理
运行时通过 LoadLibrary
和 GetProcAddress
手动加载 DLL 并获取函数地址。
2. 使用步骤
(1)不需要 .lib
文件,只需:
MyDll.dll
MyDll.h
(知道函数原型)
(2)加载 DLL 并获取函数指针
#include <windows.h>
#include <iostream>// 定义函数指针类型
typedef int (*AddFunc)(int, int);int main()
{HMODULE hDll = LoadLibrary(_T("MyDll.dll")); // 加载 DLLif (hDll == NULL) {std::cout << "无法加载 DLL!" << std::endl;return -1;}// 获取函数地址AddFunc Add = (AddFunc)GetProcAddress(hDll, "Add");if (!Add) {std::cout << "无法找到函数 Add!" << std::endl;FreeLibrary(hDll);return -1;}// 调用函数int result = Add(3, 4);std::cout << "结果:" << result << std::endl;// 卸载 DLLFreeLibrary(hDll);return 0;
}
3. 特点
优点 | 缺点 |
---|---|
运行时动态加载,灵活 | 使用复杂,需手动管理函数指针 |
可判断 DLL 是否存在,提供降级方案 | 无编译期检查,易出错(函数名拼错) |
支持插件系统、热更新、按需加载 | 性能稍低(需查表) |
程序可容忍缺失 DLL | 需要正确处理 FreeLibrary 防止内存泄漏 |
四、核心区别对比表
对比项 | 隐式链接 | 显式链接 |
---|---|---|
加载时机 | 程序启动时自动加载 | 运行时手动加载(LoadLibrary ) |
是否需要 .lib | 是 | 否(可选) |
函数调用方式 | 直接调用(如 Add(1,2) ) | 通过函数指针调用 |
启动依赖 | 必须存在 DLL,否则无法启动 | 可容忍缺失,运行时报错 |
灵活性 | 低 | 高(可动态选择、卸载、替换) |
适用场景 | 核心功能、稳定依赖 | 插件、可选模块、第三方组件 |
错误处理 | 启动失败 | 可在运行时提示用户 |
性能 | 略高 | 略低(需查找符号) |
典型 API | 无(编译器自动处理) | LoadLibrary , GetProcAddress , FreeLibrary |
五、如何选择?
场景 | 推荐方式 |
---|---|
程序核心功能依赖的 DLL(如运行库) | ✅ 隐式链接 |
第三方 SDK、硬件驱动接口 | ✅ 显式链接(容错更好) |
实现插件系统(如 Photoshop 滤镜) | ✅ 显式链接 |
需要热更新或动态替换模块 | ✅ 显式链接 |
小项目、简单调用、DLL 一定存在 | ✅ 隐式链接更方便 |
六、最佳实践建议
- 优先考虑显式链接 用于第三方或可选模块,提升程序健壮性。
- 使用 RAII 封装
HMODULE
,避免忘记FreeLibrary
:
class DllLoader {
public:DllLoader(const TCHAR* name) { hDll = LoadLibrary(name); }~DllLoader() { if (hDll) FreeLibrary(hDll); }HMODULE get() { return hDll; }
private:HMODULE hDll = nullptr;
};
- 导出 C 函数(用
extern "C"
)避免 C++ 名称修饰问题。 - 在发布程序时,确保 DLL 路径正确(当前目录、系统路径、应用程序目录等)。
七、补充:如何导出 DLL 函数?
// MyDll.cpp
extern "C" __declspec(dllexport) int Add(int a, int b)
{return a + b;
}
extern "C"
防止 C++ 编译器名称修饰,便于 GetProcAddress
查找。
总结
方式 | 何时用 | 关键词 |
---|---|---|
隐式链接 | 简单、固定依赖 | .lib 、启动加载、直接调用 |
显式链接 | 灵活、容错、插件 | LoadLibrary 、GetProcAddress 、运行时加载 |
💬 一句话总结:
- 要简单直接 → 用 隐式链接
- 要灵活可控 → 用 显式链接
显示声明与隐式声明的使用与区别
【C++】显示声明与隐式声明的使用与区别-CSDN博客文章浏览阅读1.1k次,点赞17次,收藏19次。【C++】显示声明与隐式声明的使用与区别_隐式声明https://blog.csdn.net/wangnaisheng/article/details/142489678?spm=1011.2415.3001.5331