註冊「函數 + 參數」,在滿足條件時自動調用。核心難點:參數多樣性與統一介面的矛盾——調用方不關心具體參數,只需呼叫統一的 Run()。
class Closure {
public:
virtual ~Closure() {}
virtual void Run() = 0;
protected:
Closure() {}
};
template <class T>
class Callback0 : public Closure {
typedef void (T::*Done)();
T* object_; Done run_;
public:
Callback0(T* obj, Done run) : object_(obj), run_(run) {}
void Run() override { (object_->*run_)(); }
};
template <class T, class T1>
class Callback1 : public Closure {
typedef void (T::*Done)(T1);
T* object_; Done run_; T1 arg0_;
public:
Callback1(T* obj, Done run, T1 arg) : object_(obj), run_(run), arg0_(arg) {}
void Run() override { (object_->*run_)(arg0_); }
};
// 輔助工廠函數
template <class T> Closure* NewCallback(T* obj, void (T::*m)()) {
return new Callback0<T>(obj, m);
}
template <class T, class T1> Closure* NewCallback(T* obj, void (T::*m)(T1), T1 p) {
return new Callback1<T, T1>(obj, m, p);
}
優點:型別安全、參數編譯期綁定。
缺點:模板程式碼膨脹、閱讀負擔大、C 語言難以移植。
Event 定義 virtual void fireEvent(EventArgs*)EventArgs(如 MouseEventArgs、KeyEventArgs)event->fireEvent(args)優點:無模板、符合 OOP 多態、易於 C 語言移植。
typedef struct event {
void (*fireEvent)(void* arg);
void* arg;
} event_t;
typedef struct event_arg1 { int value; } event_arg1_t;
void fireEvent_arg1(void* arg) {
event_arg1_t* a = (event_arg1_t*)arg;
printf("arg 1 = %d\n", a->value);
}
#define NewEvent(event, eventtype, callback) do { \
*(event) = malloc(sizeof(event_t)); \
(*(event))->arg = malloc(sizeof(eventtype)); \
(*(event))->fireEvent = callback; \
} while(0)
#define DestroyEvent(event) do { \
free((*(event))->arg); free(*(event)); \
} while(0)
int main() {
event_t* event;
NewEvent(&event, event_arg1_t, fireEvent_arg1);
((event_arg1_t*)event->arg)->value = 100;
event->fireEvent(event->arg);
DestroyEvent(&event);
return 0;
}
優點:純 C 實現、零模板、跨語言呼叫友好。
缺點:型別安全靠程序員自覺、需手動管理記憶體。
| 場景 | 推薦方案 |
|---|---|
| 現代 C++、追求型別安全 | 方案一或 std::function/lambda (C++11+) |
| C++ 專案、偏好 OOP、避免模板 | 方案二 |
| 純 C 專案、嵌入式、需跨語言 | 方案三 |