MFC 擴展 DLL 僅能供動態鏈接 MFC 的客戶程序使用;常規 DLL 雖通用但接口不能使用 MFC 類型。更嚴重的是 二進制兼容性 問題:
導出類
CMyClass對象大小從 30 字節擴展到 34 字節(新增int成員)。直接替換 DLL 會導致客戶程序因對象大小不匹配而崩潰。若無客戶端源碼,新 DLL 完全無法使用。
創建穩定的接口類 CMyInterface,內部聚合實現類 CMyClass 指針。客戶端僅依賴接口類。
// DLL 端頭文件
#include "MyClass.h"
class __declspec(dllexport) CMyInterface {
CMyClass* pImpl;
public:
CMyInterface(); ~CMyInterface();
int FunctionA(int);
int FunctionB(int);
};
// 客戶端頭文件(無需包含 MyClass.h)
class __declspec(dllimport) CMyInterface {
class CMyClass; // 前向聲明
CMyClass* pImpl;
public:
CMyInterface(); ~CMyInterface();
int FunctionA(int);
int FunctionB(int);
};
// DLL 端實現
CMyInterface::CMyInterface() : pImpl(new CMyClass()) {}
CMyInterface::~CMyInterface() { delete pImpl; }
int CMyInterface::FunctionA(int n) { return pImpl->FunctionA(n); }
int CMyInterface::FunctionB(int n) { return pImpl->FunctionB(n); }
優點: 實現類任意變更不影響客戶端二進制兼容。
缺點: 接口類隨功能增長變得龐大;每次調用多一次間接層,性能微損;需為每個成員函數編寫轉發代碼。
導出類本身,但隱藏構造/析構,提供靜態 CreateMe/DestroyMe 管理對象生命週期。
class __declspec(dllexport) CMyClass {
CMyClass(); ~CMyClass(); // 私有
public:
static CMyClass* CreateMe();
static void DestroyMe(CMyClass* ptr);
// 業務方法...
};
// 實現
CMyClass* CMyClass::CreateMe() { return new CMyClass; }
void CMyClass::DestroyMe(CMyClass* ptr) { delete ptr; }
// 客戶端使用
CMyClass* pObj = CMyClass::CreateMe();
pObj->DoSomething();
CMyClass::DestroyMe(pObj);
優點: 對象大小變更不影響客戶端(客戶端只持有指針);無需額外接口類;性能無額外轉發開銷。
缺點: 客戶端必須嚴格遵守 CreateMe/DestroyMe 配對調用,否則內存洩漏或重複釋放;無法在棧上創建對象。
| 場景 | 推薦方案 |
|---|---|
| 導出類接口穩定、成員較少 | 直接導出類(最簡單) |
| 導出類頻繁變更、成員多、需高度二進制兼容 | 接口類模式 |
| 導出類較大、不想編寫轉發代碼、可接受工廠模式約束 | 靜態工廠函數 |
| 跨編譯器調用(VC++ DLL 供 VB/Delphi/C# 用) | 導出純 C 函數 + 面向對象內部實現 |
動態鏈接 MFC 的常規 DLL 導出函數首行必須添加:
extern "C" __declspec(dllexport) int MyFunction(int x) {
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 切換模塊狀態
// 業務邏輯...
}
靜態鏈接 MFC 的常規 DLL 此宏無效但無害,建議統一添加以便遷移。