| 維度 | .lib 靜態庫 | .dll 動態庫 |
|---|---|---|
| 鏈接時機 | 編譯時(靜態鏈接) | 運行時(動態鏈接) |
| 代碼副本 | 複製到每個可執行文件 | 內存中單份副本共享 |
| 文件體積 | 較大(含實現代碼) | 較小(僅導出表) |
| 部署依賴 | 無需額外文件 | 需隨程序分發 .dll |
| 升級維護 | 需重新編譯分發主程序 | 僅替換 .dll 即可(接口兼容前提) |
| 內存效率 | 多進程各載入副本 | 多進程共享內存頁 |
| 調試難度 | 較簡單 | 需加載符號表、斷點設置複雜 |
#include發布時:僅需 .exe + .dll,.h 和 .lib 僅開發階段需要。
// MyDLL.h
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
MYDLL_API void ExportedFunction();
MYDLL_API class ExportedClass { ... };
MYDLL_EXPORTS 宏 → dllexportdllimport#include "MyDLL.h" + 鏈接 MyDLL.libExportedFunction();// 運行時動態加載
HMODULE hDll = LoadLibrary(L"MyDLL.dll");
if (hDll) {
typedef void (*FuncPtr)();
FuncPtr func = (FuncPtr)GetProcAddress(hDll, "ExportedFunction");
if (func) func();
FreeLibrary(hDll);
}
__stdcall/__cdecl)一致,否則棧平衡錯誤導致崩潰.def 文件可導出未修飾函數名,便於 GetProcAddress// DLL導出函數
__declspec(dllexport) void SetString(std::string& s) {
s = "Hello from DLL"; // DLL內部分配內存
}
// EXE調用
std::string str;
SetString(str); // EXE中析構時釋放內存 → 崩潰!
「誰分配,誰釋放」原則被違反:DLL與EXE擁有各自獨立的堆(除非雙方都動態鏈接同一 CRT)。DLL分配的內存,EXE析構時嘗試釋放 → 堆破壞 → 崩潰。
CreateXXX() 分配 → ReleaseXXX() 釋放,同模塊內完成void GetString(char* buf, int size); 由 EXE 分配、傳入、釋放MSVCRT.dll 堆,可跨模塊 new/delete(但版本必須完全一致)IUnknown::AddRef/Release 管理生命週期項目 → 右鍵屬性 → 配置屬性 → 常規 → 配置類型: - 靜態庫 (.lib) → 生成靜態庫 - 動態庫 (.dll) → 生成 DLL + 導入庫 .lib - 應用程序 (.exe) → 生成可執行文件
需要跨進程共享代碼/數據?
├─ 是 → 必選 DLL
└─ 否 → 接口頻繁變更/插件架構?
├─ 是 → DLL(熱插拔、獨立升級)
└─ 否 → 性能極度敏感/單一發布?
├─ 是 → 靜態庫(無加載開銷、鏈接時優化)
└─ 否 → 團隊習慣/項目規範決定
__declspec(dllexport)__stdcall) 雙端一致