C++實現委托和消息反饋模板:
繼承+多態
乍一看是理所當然的選擇,庫中的類把響應處理函數設置為虛函數,客戶程序可以繼承這個類並且重載響應函數。以某個Socket類為例,可以提供一個OnRecv函數用來響應網絡數據包到達的處理。客戶程序只需要重載OnRecv並進行自己的處理就可以了。
- struct Socket { // base class
- virtual void OnRecv();
- };
- stuct MySocket { // your event-handle class
- virtual void OnRecv() { /* do sth here ... */ }
- }
疑問:很多時候這樣做實在很煩,特別是做小程序的時候,或者需要快速做原型的時候,一眼望去小小的程序一上來就繼承了一大堆東西,頗為不爽。只是想著能省事一點,希望能像那些腳本語言一樣快速綁定消息響應,而不是以繼承開始工作——我已經害怕看到長長的類繼承樹了,很多時候根本不必要繼承整個類;又或者某些類只提供一個接口而不是具體的類又或者需要多重繼承,處理都有一定麻煩;最麻煩的莫過於有時候需要改變響應處理,難道繼承好幾個下來麼——這麼多虛表也是浪費啊。
點評:為了使用Socket就必須繼承Socket,這可以說是Socket的設計的問題。如果需要實現類似的功能的話,可以寫成如下,雖然和繼承 Socket 沒有多少本質的差別,不過確實把消息處理類和Socket的實現扯開了。
- struct SocketEventHandler {
- virtual void OnRecv() { /* ... */ }
- virtual void OnSend() { /* ... */ }
- };
- struct Socket {
- void set_handler( SocketEventHandler* h ) { handler_ = h; }
- private:
- SocketEventHandler* handler_;
- };
- struct MyHandler : SocketEventHandler {
- void OnRecv() { ... }
- };
- Socket s;
- MyHandler h;
- s.set_handler( &h );
丟開繼承,我們有沒有一種簡單明確的表達方法呢?我不禁想起了c時代的回調函數……
回調函數(CallBack)
非常簡單,就是一個函數指針。剛才的OnRecv可以寫成這樣
- struct Socket {
- void OnRecv() { if(OnRecvHandle!=NULL) OnRecvHandle(); }
- void (*OnRecvHandle) ();
- };
客戶程序只需要編寫一個MyOnRecv函數,並且賦值給OnRecvHandle就可以了
- void MyOnRecv(); // your event-handle function
- Socket foo;
- foo.OnRecvHandle = MyOnRecv;
疑問:非常簡單,不需要繼承類就可以處理,而且隨時可以替換不同的處理函數。其實多態的本質也是函數指針,只不過多態是用vtable統一管理函數指針。回調函數要特別注意函數指針是否為空的問題,因此最好外面在包裝一層判斷過程,回調函數最大問題在於類型不安全。