回調函數是 C/C++ 實現「控制反轉」、解耦演算法與具體處理邏輯的核心技術。本文從函數指標基礎語法出發,透過「檔案上傳進度回調」實戰案例,展示如何將「資料傳輸」與「介面顯示」分離,實現高內聚、低耦合的模組化設計。
// 1. 定義函數指標型別
typedef int (*CallBack)(char *p);
// 2. 回調函數實作
int A(char *p) { /* 處理邏輯 */ return 0; }
// 3. 使用回調的操作函數
void B(CallBack lpCall, char *pProvide) {
// ... 自身邏輯 ...
lpCall(pProvide); // 呼叫回調
// ... 後續邏輯 ...
}
// 4. 實際呼叫
char *p = "hello!";
CallBack myCallBack = A;
B(myCallBack, p);
class CUploadFile : public CDialog {
int Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath);
static int GetCurState(int nCurDone, int nInAll, void *pParam); // 靜態成員作為回調
};
int CUploadFile::Send(...) {
int ret = Upload(data, GetCurState, this, ...); // 傳入 this 指標
}
int CUploadFile::GetCurState(int nCurData, int nInAll, void *pParam) {
CUploadFile* pThis = (CUploadFile*)pParam;
// 直接操作 pThis 介面控件 (進度條、文字)
return 0;
}
缺點:CUploadFile 同時負責網路傳輸與 UI 更新,難以復用、單元測試、替換介面實現。
class CUploadData {
public:
typedef int (*SetUploadCaller)(int nCurData, int nInAll, void *pParam);
int UploadFile(LPCTSTR lpFileNamePath, LPVOID lparam, SetUploadCaller Caller);
int Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath);
// 移除 GetCurState,不再包含任何 UI 邏輯
};
class CShowGUI : public CDialog {
public:
typedef int (*SetUploadCaller)(int nCurData, int nInAll, void *pParam);
void SetCallBack(LPCTSTR strPath);
static int GetCurState(int nCurData, int nInAll, void *pParam); // 介面層回調
CUploadData m_UploadData; // 組合關係
};
void CShowGUI::SetCallBack(LPCTSTR strPath) {
SetUploadCaller myCaller = GetCurState; // 取得介面層回調位址
m_UploadData.UploadFile(strPath, this, myCaller); // 傳入 this 供回調使用
}
int CShowGUI::GetCurState(int nCurData, int nInAll, void *pParam) {
CShowGUI* pThis = (CShowGUI*)pParam;
// 更新進度條、顯示文字、計算速度等純 UI 操作
return 0;
}
| 模式 | 角色對應 | 優勢 |
|---|---|---|
| 回調函數 | CUploadData = 演算法框架, CShowGUI::GetCurState = 具體策略 | 編譯期靜態綁定,零虛函數開銷 |
| 策略模式 | 介面 IUploadObserver + 多種實現 | 執行期動態切換,支援多觀察者 |
| 觀察者模式 | Subject/Observer 介面 + 訂閱/發布 | 一對多通知,鬆耦合 |
C++ 非靜態成員函數帶有隱含 this 指標,無法直接轉為普通函數指標。解決方案:
void* pParam 傳遞 this (本文採用)pParam 指向的物件仍有效PostMessage 或同步機制切換至 UI 執行緒回調函數是 C/C++ 實現「好萊塢原則」——Don't call us, we'll call you 的基石。透過函數指標將「控制流」與「業務邏輯」分離,配合 void* pParam 傳遞上下文,即可構建高度可復用、可測試、可擴展的模組化架構。現代 C++ 建議優先使用 std::function + lambda,獲得型別安全與閉包捕獲能力。