什么是信號(hào)槽?
- 簡(jiǎn)單來說,信號(hào)槽是觀察者模式的一種實(shí)現(xiàn),或者說是一種升華。
- 一個(gè)信號(hào)就是一個(gè)能夠被觀察的事件,或者至少是事件已經(jīng)發(fā)生的一種通知;一個(gè)槽就是一個(gè)觀察者,通常就是在被觀察的對(duì)象發(fā)生改變的時(shí)候——也可以說是信號(hào)發(fā)出的時(shí)候——被調(diào)用的函數(shù);你可以將信號(hào)和槽連接起來,形成一種觀察者-被觀察者的關(guān)系;當(dāng)事件或者狀態(tài)發(fā)生改變的時(shí)候,信號(hào)就會(huì)被發(fā)出;同時(shí),信號(hào)發(fā)出者有義務(wù)調(diào)用所有注冊(cè)的對(duì)這個(gè)事件(信號(hào))感興趣的函數(shù)(槽)。
- 信號(hào)和槽是多對(duì)多的關(guān)系。一個(gè)信號(hào)可以連接多個(gè)槽,而一個(gè)槽也可以監(jiān)聽多個(gè)信號(hào)。
- 另外信號(hào)可以有附加信息。
使用信號(hào)槽
- 信號(hào)槽是偉大的工具,但是如何能更好的使用它?相比于直接函數(shù)調(diào)用,有三點(diǎn)值得我們的注意。
- 一個(gè)信號(hào)槽的調(diào)用,可能會(huì)比直接函數(shù)調(diào)用耗費(fèi)更多的時(shí)間/空間;
- 可能不能使用 inline;
- 對(duì)于代碼閱讀者來說可能并不友好。
- 使用信號(hào)槽進(jìn)行解耦,我們獲得的最大的好處是,連接兩端的對(duì)象不需要知道對(duì)方的任何信息。
- 你可以實(shí)現(xiàn)一個(gè)應(yīng)用程序,其中每一個(gè)函數(shù)調(diào)用都是通過信號(hào)來觸發(fā)的。這在技術(shù)上說是完全沒有問題的,然而卻是不大可行的,因?yàn)樾盘?hào)槽的使用無疑會(huì)喪失一部分代碼可讀性和系統(tǒng)性能。如何在這其中做出平衡,也是你需要考慮的很重要的一點(diǎn)。
sigslot庫(kù)
C++中的信號(hào)槽系統(tǒng)常用的有三種:boost的signals,sigslot,sigc++。其中sigslot庫(kù)是比較簡(jiǎn)單好用的。
sigslot是一個(gè)線程安全、類型安全,用C++實(shí)現(xiàn)的sig/slot機(jī)制(sig/slot機(jī)制就是對(duì)象之間發(fā)送和接收消息的機(jī)制)的開源代碼庫(kù)。只有一個(gè)頭文件sigslot.h。
-
基本功能有:
- connect
- disconnect
- emit
-
sigslot優(yōu)點(diǎn)
- 不用擔(dān)心空回調(diào),當(dāng)回調(diào)對(duì)象析構(gòu)時(shí)會(huì)自動(dòng)disconnect
- 支持多線程,線程安全,有鎖
-
sigslot缺點(diǎn)
- 只能回調(diào)void類型函數(shù),不支持返回值。boost中的signals庫(kù)架構(gòu)類似,支持返回值,但引入了boost中的其他庫(kù)
- slot沒有優(yōu)先級(jí),不能動(dòng)態(tài)調(diào)整回調(diào)隊(duì)列中的先后順序
-
slot函數(shù)(被回調(diào)的函數(shù))就是普通的成員函數(shù),但有以下限制:
- 返回值必須為void
- slot參數(shù)個(gè)數(shù)范圍為0-8個(gè)
- 實(shí)現(xiàn)slot的類必須繼承自has_slots<>
前兩條是sigslot庫(kù)作者的限制,作者權(quán)衡各方面因素后做出的決定,如果你覺得有必要你可以修改sigslot代碼取消該限制,而最后一條是sigslot的機(jī)制基礎(chǔ),必須遵守,除非你自己重新寫個(gè)sigslot。
需要注意的是:sigslot庫(kù)的設(shè)計(jì),當(dāng)發(fā)送一個(gè)沒有連接的信號(hào)時(shí),不做任何處理,也不會(huì)有錯(cuò)誤發(fā)出。
基本使用方式
- 包含頭文件
#include "sigslot.h"
- 改動(dòng)(“typename 必須前置于嵌套依賴類型名”)
//在sigslot.h的420,將:
typedef sender_set::const_iterator const_iterator;
//改為:
typedef typename sender_set::const_iterator const_iterator;
- signal0~signal8:信號(hào)類:作為類成員
class mySg
{
sigc::signal0<> sg1; // 無參數(shù)
sigc::signal2<char*, double=""> sg2; // 2個(gè)參數(shù)
}
- connection(槽函數(shù):作為類成員,類需要繼承has_slots<>,且槽函數(shù)的返回值必須是void類型)
class mySlot: public : has_slots<>
{
public:
void on_func1(){} // 無參數(shù),與信號(hào)對(duì)應(yīng)
void on_func2(char*, double)(){} // 2個(gè)參數(shù)
};
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(發(fā)送信號(hào):可以直接使用()運(yùn)算符,也可以調(diào)用signal的emit函數(shù))
sig.sg1.emit();
sig.sg2("str",0.1);