函式庫常需通知客戶端事件 (如網路封包到達、UI 點擊)。傳統方案:
OnRecv()。缺點:強迫繼承、多重繼承麻煩、難以動態切換處理邏輯。void (*OnRecvHandle)()。缺點:型別不安全、無法綁定成員函式/仿函式。委託本質:型別安全的動態訊息轉發機制。Java/C# 內建支援,C++ 需自行實現。核心難點:如何儲存任意可呼叫實體 (函式指標、仿函式、成員函式) 並統一呼叫。
// 介面層:統一呼叫入口
struct DelegationInterface {
virtual ~DelegationInterface() {}
virtual void Action() = 0;
};
// 實作層:模板適配具體接收者
template <class T>
struct DelegationImpl : public DelegationInterface {
T functor_;
DelegationImpl(T f) : functor_(f) {}
virtual void Action() { functor_(); } // 統一呼叫 operator()
};
// 信號類別:管理連線與觸發
struct Signal {
DelegationInterface* impl_ = nullptr;
~Signal() { delete impl_; }
void operator()() { if (impl_) impl_->Action(); }
template <class T>
void ConnectSlot(T slot) {
delete impl_;
impl_ = new DelegationImpl<T>(slot);
}
};
使用範例:
struct Socket { Signal OnRecv; };
void OnRecvFunc() { /* ... */ }
struct Functor { void operator()() { /* ... */ } };
Socket s;
s.OnRecv.ConnectSlot(OnRecvFunc); // 綁定函式
s.OnRecv.ConnectSlot(Functor()); // 綁定仿函式
s.OnRecv(); // 觸發
template <class T>
struct DelegationImpl : public DelegationInterface {
using MemFn = void (T::*)();
T* obj_;
MemFn fn_;
DelegationImpl(T* o, MemFn f) : obj_(o), fn_(f) {}
virtual void Action() { if (obj_) (obj_->*fn_)(); }
};
struct Signal {
DelegationInterface* impl_ = nullptr;
// ...
template <class T>
void ConnectSlot(T& obj, void (T::* fn)()) {
delete impl_;
impl_ = new DelegationImpl<T>(&obj, fn);
}
};
// 使用
struct MsgBox { void Show() { /* ... */ } };
MsgBox box;
Socket s;
s.OnRecv.ConnectSlot(box, &MsgBox::Show);
將 impl_ 改為 std::vector<DelegationInterface*>,ConnectSlot 推入向量,operator() 迴圈呼叫所有槽位。
C++ 模板無法直接支援可變參數委託,需為每種參數組合撰寫獨立介面/實作 (如 DelegationInterface<P1>, DelegationInterface<P1,P2>)。解決方案:
std::tuple (C++11)std::function + 可變參數模板橋接委託以「介面隔離細節、模板適配型別、多型統一呼叫」實現型別安全的動態綁定。雖參數化仍受限於模板機制,但現代 C++ (Boost, C++17/20) 已提供成熟解決方案。理解其原理有助於設計靈活的事件系統。