首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C/C++ 回調函數入門:原理、應用場景與 C++ 成員函數適配

     編輯:關於C++

什麼是回調函數?

簡單說:**回調函數是透過函數指標被呼叫的函數**。當你將函數位址作為參數傳給另一函數,並在該函數內透過指標調用時,即構成回調。核心在於「將呼叫者與被呼叫者解耦」——呼叫者只需知道函數原型,不關心具體實現。

為什麼需要回調?

1. 泛型演算法庫(策略模式的前身)

排序庫不想內建比較邏輯,支援 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);
}

同一排序函數,透過回調支援任意型別。

2. 事件通知機制

  • SetTimer:到時透過回調或發送 WM_TIMER 訊息通知
  • EnumWindows:列舉頂層視窗,對每個視窗呼叫回調,回傳 FALSE 可提前終止

回調讓「事件產生者」不需知道「事件消費者」細節,僅約定原型與回傳值語義。

C++ 中的定位:慎用,優先考慮虛擬函數/仿函數

回調源自 C 語言。現代 C++ 應優先使用:

  • 虛擬函數:介面繼承,型別安全
  • 仿函數:std::function/Lambda,支援狀態捕獲

僅在以下情境使用回調:

  1. 與 C 程式碼介接
  2. 對接現有回調介面 API(如 Win32、第三方 C 庫)

呼叫慣例:__stdcall vs __cdecl

慣例堆疊清理用途關鍵字
__stdcall (Pascal)被呼叫者Win32 API 統一標準(可變參數除外)__stdcall、CALLBACK、PASCAL
__cdecl (C/C++)呼叫者C/C++ 預設、可變參數函數__cdecl

Win32 採用 __stdcall 因早期 Windows 記憶體極小(640KB),被呼叫者清理堆疊可減少每個呼叫點的清理代碼,縮小執行檔體積。

C++ 成員函數作為回調的挑戰

問題:隱含 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,函數指標型別不同。

解決方案

  1. 靜態成員函數:無 this,直接相容
  2. 全域/靜態包裝函數 + void* 上下文:
    
    int __stdcall CmpWrapper(const void* a, const void* b, void* ctx) {
        auto* self = static_cast(ctx);
        return self->Compare(*(int*)a, *(int*)b);
    }
    
  3. C++11 std::function + Lambda 捕獲 this(若庫支援)

完整範例:DLL 排序庫與回調

DLL 端 (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); // 升序
// 若需降序,僅換回調函數即可,排序邏輯完全不動

總結

  • 回調 = 函數指標 + 介面約定,核心價值在於解耦
  • Win32 統一使用 __stdcall,跨模組回調必須匹配呼叫慣例
  • C++ 非靜態成員函數不能直接作回調,需靜態包裝或改用現代 C++ 技術
  • 新代碼優先用 std::function/Lambda/虛擬函數,僅在 C 互操作層使用原始回調
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved