在大型 C++ 專案開發中,回調函數與函數指針是實現模組解耦、擴展底層庫功能的核心機制。初次接觸時往往只在書本見過聲明語法,實際專案如何運用卻一頭霧水。本文將從基礎語法入手,逐步剖析企業級專案中的實戰模式,幫助你建立清晰的認知體系。
函數指針指向可執行代碼的內存地址,其聲明格式為:
void (*p)(); // p 是指向無參、返回 void 函數的指針
若函數帶參數、有返回值,聲明相應調整,例如:
int (*funcPtr)(int, double); // 指向接受 int、double 並返回 int 的函數
定義一個主調函數 caller,接受函數指針作為參數:
void caller(void(*ptr)()) {
// 執行某些邏輯
if (ptr) ptr(); // 調用回調
}
回調函數只是一個普通函數:
void func() {
std::cout << "Callback executed!" << std::endl;
}
調用時將函數名(即地址)傳入:
int main() {
caller(func); // 將 func 的地址傳給 caller
return 0;
}
在工業級代碼中,通常使用 typedef 定義函數指針類型,提升可讀性並便於跨模組傳遞:
// 定義一種回調類型:返回 bool,採用 __stdcall 調用約定 typedef bool (CALLBACK * LoadLibraryExFailCallBack)(const std::wstring &path, int stage);
全局或類靜態成員保存回調指針,初始化為 NULL 防止野指針:
LoadLibraryExFailCallBack g_callBack = NULL;
底層提供安裝回調的函數,僅做指針轉存:
void __declspec InstallLoadLibraryExFailCallBack(LoadLibraryExFailCallBack callback) {
g_callBack = callback; // 只保存地址,不調用
}
這體現「好萊塢原則」:Don't call us, we'll call you. 底層不依賴上層具體實現,運行時由上層注入行為。
上層根據業務編寫具體回調函數:
bool __stdcall LoadLibraryMsgCallBack(const std::wstring &path, int stage) {
// 記錄日誌、彈窗提示、重試邏輯等
return true; // 返回處理結果
}
啟動時註冊:
InstallLoadLibraryExFailCallBack(LoadLibraryMsgCallBack);
之後底層發生加載失敗時,直接調用 g_callBack(path, stage) 即可觸發上層邏輯。若需更換行為,只需再實現一個同簽名函數並重新註冊,底層代碼零修改。
typedef 完全一致,含調用約定(__stdcall/__cdecl)。掌握回調與函數指針,是從「會用庫」進階為「設計庫」的關鍵一步。下次看到 typedef void (*Callback)(...),你就知道這是一個留給使用者的「行為插槽」。