簡單說:**回調函數是透過函數指標被呼叫的函數**。當你將函數位址作為參數傳給另一函數,並在該函數內透過指標調用時,即構成回調。核心在於「將呼叫者與被呼叫者解耦」——呼叫者只需知道函數原型,不關心具體實現。
排序庫不想內建比較邏輯,支援 int/float/string 等任意型別:
// 庫定義
typedef int (__stdcall *CompareFn)(const void*, const void*);
void BubbleSort(void* arr, int n, int sz, CompareFn cmp);
void QuickSort(void* arr, int n, int sz, CompareFn cmp);
使用者提供比較函數:
int __stdcall CmpInt(const void* a, const void* b) {
int x = *(int*)a, y = *(int*)b;
return (x < y) ? -1 : (x > y) ? 1 : 0;
}
int __stdcall CmpStr(const void* a, const void* b) {
return strcmp((char*)a, (char*)b);
}
同一排序函數,透過回調支援任意型別。
SetTimer:到時透過回調或發送 WM_TIMER 訊息通知EnumWindows:列舉頂層視窗,對每個視窗呼叫回調,回傳 FALSE 可提前終止回調讓「事件產生者」不需知道「事件消費者」細節,僅約定原型與回傳值語義。
回調源自 C 語言。現代 C++ 應優先使用:
std::function/Lambda,支援狀態捕獲僅在以下情境使用回調:
__stdcall vs __cdecl| 慣例 | 堆疊清理 | 用途 | 關鍵字 |
|---|---|---|---|
__stdcall (Pascal) | 被呼叫者 | Win32 API 統一標準(可變參數除外) | __stdcall、CALLBACK、PASCAL |
__cdecl (C/C++) | 呼叫者 | C/C++ 預設、可變參數函數 | __cdecl |
Win32 採用 __stdcall 因早期 Windows 記憶體極小(640KB),被呼叫者清理堆疊可減少每個呼叫點的清理代碼,縮小執行檔體積。
this 指標
class Worker {
public:
int __stdcall Compare(int a, int b) { return a - b; }
};
Worker w;
BubbleSort(arr, n, sz, &Worker::Compare); // 編譯錯誤!
錯誤:int (__stdcall Worker::*)(int,int) 無法轉換為 int (__stdcall*)(int,int)。非靜態成員函數擁有隱含 this,函數指標型別不同。
this,直接相容void* 上下文:
int __stdcall CmpWrapper(const void* a, const void* b, void* ctx) {
auto* self = static_cast(ctx);
return self->Compare(*(int*)a, *(int*)b);
}
std::function + Lambda 捕獲 this(若庫支援)sort.dll)
typedef int (__stdcall *CompareFn)(const BYTE*, const BYTE*);
void __stdcall BubbleSort(BYTE* arr, int n, int sz, CompareFn cmp) {
for (int i=0; i 0)
swap_bytes(arr+j*sz, arr+(j+1)*sz, sz);
}
int arr[] = {5432, 4321, 3210, 2109, 1098};
BubbleSort((BYTE*)arr, 5, sizeof(int), &CmpInt); // 升序
// 若需降序,僅換回調函數即可,排序邏輯完全不動
__stdcall,跨模組回調必須匹配呼叫慣例std::function/Lambda/虛擬函數,僅在 C 互操作層使用原始回調