什麼是訊號槽?
- 簡單來說,訊號槽是觀察者模式的一種實現,或者說是一種昇華。
- 一個訊號就是一個能夠被觀察的事件,或者至少是事件已經發生的一種通知;一個槽就是一個觀察者,通常就是在被觀察的物件發生改變的時候——也可以說是訊號發出的時候——被呼叫的函式;你可以將訊號和槽連線起來,形成一種觀察者-被觀察者的關係;當事件或者狀態發生改變的時候,訊號就會被髮出;同時,訊號發出者有義務呼叫所有註冊的對這個事件(訊號)感興趣的函式(槽)。
- 訊號和槽是多對多的關係。一個訊號可以連線多個槽,而一個槽也可以監聽多個訊號。
- 另外訊號可以有附加資訊。
使用訊號槽
- 訊號槽是偉大的工具,但是如何能更好的使用它?相比於直接函式呼叫,有三點值得我們的注意。
- 一個訊號槽的呼叫,可能會比直接函式呼叫耗費更多的時間/空間;
- 可能不能使用 inline;
- 對於程式碼閱讀者來說可能並不友好。
- 使用訊號槽進行解耦,我們獲得的最大的好處是,連線兩端的物件不需要知道對方的任何資訊。
- 你可以實現一個應用程式,其中每一個函式呼叫都是通過訊號來觸發的。這在技術上說是完全沒有問題的,然而卻是不大可行的,因為訊號槽的使用無疑會喪失一部分程式碼可讀性和系統效能。如何在這其中做出平衡,也是你需要考慮的很重要的一點。
##sigslot庫
-
C++中的訊號槽系統常用的有三種:boost的signals,sigslot,sigc++。其中sigslot庫是比較簡單好用的。
-
sigslot是一個執行緒安全、型別安全,用C++實現的sig/slot機制(sig/slot機制就是物件之間傳送和接收訊息的機制)的開原始碼庫。只有一個標頭檔案sigslot.h。
-
基本功能有:
- connect
- disconnect
- emit
-
sigslot優點
- 不用擔心空回撥,當回撥物件析構時會自動disconnect
- 支援多執行緒,執行緒安全,有鎖
-
sigslot缺點
- 只能回撥void型別函式,不支援返回值。boost中的signals庫架構類似,支援返回值,但引入了boost中的其他庫
- slot沒有優先順序,不能動態調整回撥佇列中的先後順序
-
slot函式(被回撥的函式)就是普通的成員函式,但有以下限制:
- 返回值必須為void
- slot引數個數範圍為0-8個
- 實現slot的類必須繼承自has_slots<>
-
前兩條是sigslot庫作者的限制,作者權衡各方面因素後做出的決定,如果你覺得有必要你可以修改sigslot程式碼取消該限制,而最後一條是sigslot的機制基礎,必須遵守,除非你自己重新寫個sigslot。
-
需要注意的是:sigslot庫的設計,當傳送一個沒有連線的訊號時,不做任何處理,也不會有錯誤發出。
基本使用方式
- 包含標頭檔案
#include "sigslot.h"
複製程式碼
- 改動(“typename 必須前置於巢狀依賴型別名”)
//在sigslot.h的420,將:
typedef sender_set::const_iterator const_iterator;
//改為:
typedef typename sender_set::const_iterator const_iterator;
複製程式碼
- signal0~signal8:訊號類:作為類成員
class mySg
{
sigc::signal0<> sg1; // 無引數
sigc::signal2<char*, double=""> sg2; // 2個引數
}
複製程式碼
- connection(槽函式:作為類成員,類需要繼承has_slots<>,且槽函式的返回值必須是void型別)
class mySlot: public : has_slots<>
{
public:
void on_func1(){} // 無引數,與訊號對應
void on_func2(char*, double)(){} // 2個引數
};
mySg sig;
mySlot slt;
sig.sg1.conncent(&slt,&mySlot::on_func1);
sig.sg2.conncent(&slt,&mySlot::on_func2);
複製程式碼
- disconnection(解除連線:可以使用disconnect()和disconnect_all())
sig.sg1.disconnect(&slt);
sig.sg1.disconnect_all();
複製程式碼
- emiting(傳送訊號:可以直接使用()運算子,也可以呼叫signal的emit函式)
sig.sg1.emit();
sig.sg2("str",0.1);
複製程式碼