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

C++ 回呼函式完整指南:機制、類型與實作模式

     編輯:關於C++

回呼函式是實現模組解耦、異步通知、泛型演算法的關鍵技術。本文系統解析回呼的內在機制、與虛擬函式/訊息/事件的關係、三種調用模式、C 語言函式指標實作、C++ 物件導向實作、Windows 訊息機制、CORBA 分散式回呼,並提供標準庫 qsort/bsearch 實戰範例。

一、回呼函式本質

定義:調用者將自身函式位址作為參數傳給被調用者(通常為庫函式/API),被調用者在特定時機透過該位址「回頭呼叫」調用者的函式。

三步驟:

  1. 宣告回呼函式原型(含呼叫慣例 __stdcall/CALLBACK)。
  2. 實作回呼函式邏輯。
  3. 註冊/安裝:將函式位址傳給被調用者。

鉤子函式:SetWindowsHookEx 專用的回呼函式特例。

二、回呼 vs 訊息 vs 事件

機制本質同步性解耦程度典型場景
回呼函式函式指標直接調用同步中(雙方知曉原型)排序比較、進度通知、定時器
Windows 訊息訊息隊列分派同步/非同步高(僅知視窗句柄)UI 互動、執行緒間通訊
事件委派/介面封裝同步高(介面契約)元件模型、C#/Java 事件

訊息機制可視為作業系統層面的「廣義回呼」;事件是物件導向語言將函式指標封裝為型別安全的「正統回呼」。

三、三種調用模式

  1. 同步調用:阻塞等待被調用者完成(普通函式調用)。
  2. 回呼調用:雙向,被調用者主動調用調用者提供的函式。
  3. 非同步調用:被調用者透過訊息/事件/執行緒主動通知調用者,調用者不阻塞。

回呼常作為非同步註冊機制(如 SetTimer(hwnd, id, elapsed, TimerProc)),非同步通知則用 PostMessage/執行緒實現。

四、C 語言:函式指標實作

1. 函式指標定義

void Func(char *s);           // 原型
void (*pFunc)(char *);        // 指標變數
typedef void (*Callback)(char *); // 簡化型別

2. 被調用者介面

void GetCallback(Callback cb) { /* ... */ cb("data"); }

3. 調用者實作與註冊

void MyCallback(char *s) { /* 處理 s */ }
GetCallback(MyCallback); // 函式名即位址

4. 呼叫慣例相容性

__stdcall int Callee(int);
void Caller(__cdecl int(*ptr)(int)); // 錯誤:慣例不符
__cdecl int(*p)(int) = Callee;       // 編譯失敗

必須確保雙方呼叫慣例一致(__cdecl/__stdcall/__fastcall)。

5. 標準庫實戰:qsort / bsearch

void qsort(void *base, size_t n, size_t w, int (*cmp)(const void*, const void*));
void *bsearch(const void *key, const void *base, size_t n, size_t w, int (*cmp)(const void*, const void*));

int cmp_int(const void *a, const void *b) { return *(int*)a - *(int*)b; }

int main() {
    int a[] = {54,21,11,67,22};
    qsort(a, 5, sizeof(int), cmp_int);
    // a = {11,21,22,54,67}
}

五、C++ 物件導向:介面/虛擬函式實作

// 介面定義
class ICallback {
public:
    virtual void OnEvent(const std::string& data) = 0;
    virtual ~ICallback() {}
};

// 被調用者持有介面指標
class Worker {
    ICallback *cb_;
public:
    void SetCallback(ICallback *cb) { cb_ = cb; }
    void DoWork() { if (cb_) cb_->OnEvent("result"); }
};

// 調用者實作介面
class MyHandler : public ICallback {
public:
    void OnEvent(const std::string& data) override { /* ... */ }
};

// 使用
Worker w; MyHandler h; w.SetCallback(&h); w.DoWork();

優點:型別安全、支援多重回呼、可攜帶狀態(類別成員)、自然支援多態。

六、Windows 訊息機制作為回呼

// 註冊視窗類時設定 WndProc(回呼入口)
// 發送同步訊息:SendMessage(hwnd, WM_USER, wParam, lParam) -> 直接呼叫 WndProc
// 發送非同步訊息:PostMessage(hwnd, WM_USER, wParam, lParam) -> 排入隊列,稍後處理

適用於 UI 執行緒與工作執行緒通訊、跨進程通訊(需 WM_COPYDATA)。

七、分散式回呼:CORBA

  • 回呼物件:客戶端實作伺服端定義的 IDL 介面,傳遞物件參考給伺服端。
  • 事件服務/通知服務:標準化的鬆耦合發布/訂閱機制,支援過濾、持久化、QoS。
  • 簡單場景用回呼介面,複雜場景用事件/通知服務。

八、最佳實踐清單

  1. 回呼函式不可長時間阻塞(會卡住被調用者)。
  2. 明確文檔化執行緒上下文(哪個執行緒呼叫回呼)。
  3. 提供註銷/移除機制,防止懸空指標/使用已釋放物件。
  4. C++ 優先用 std::function/lambda/介面,避免裸函式指標。
  5. 跨模組(DLL/進程)注意呼叫慣例與二進制相容性(extern "C"、COM)。

回呼是「好萊塢原則」——Don't call us, we'll call you——在軟體工程的具體落地,掌握其變體與適用場景,是架構師的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved