信號槽是Qt框架中一個重要的部分,主要用來解耦一組互相協作的類,使用起來非常方便。項目中有同事引入了第三方的信號槽機制,其實Boost本身就有信號/槽,而且Boost的模塊相對來說更穩定。
signals2基於Boost裡另一個庫signals實現了線程安全的觀察者模式。signal中一個比較重要的操作函數是connect,它把插槽連接到信號上;插槽可以是任意可調用對象,包括函數指針、函數對象,以及他們的bind/lambda表達式和function對象。connect函數將返回一個connection對象,表示了信號和插槽之間的連接關系,connection對象可以更靈活的處理信號與槽函數的連接、斷開等關系。
以下是一個使用Boost信號/槽的小例子:
#include "stdafx.h" #include <iostream> #include <boost/signals2.hpp> #include <boost/bind.hpp> using namespace std; using namespace boost::signals2; void slots1() { cout<<"slots1 called"<<endl; } void slots2() { cout<<"slots2 called"<<endl; } class A { public: static int staticMemberFunc(int param) { cout<<"A::staticMemberFunc called, param: "<<param<<endl; return 0; } int memberFunc(int param) { cout<<"A::memberFunc called, param: "<<param<<endl; return 0; } }; int main() { boost::signals2::signal<void()> sig; boost::signals2::signal<int(int)> sig2; A a; connection c1 = sig.connect(&slots1); connection c2 =sig.connect(&slots2); cout<<"First call-------------------"<<endl; sig(); if (c2.connected()) { c2.disconnect(); } cout<<"Second call-------------------"<<endl; sig(); connection c3 =sig2.connect(&A::staticMemberFunc);// 綁定成員函數 connection c4 =sig2.connect(boost::bind(&A::memberFunc, &a, _1));// 綁定靜態成員函數 cout<<"Return code is: "<<*sig2(44)<<endl;// 只能返回最後被調用的插槽的返回值 return 0; }
//Output:
First call-------------------
slots1 called
slots2 called
Second call-------------------
slots1 called
A::staticMemberFunc called, param: 44
A::memberFunc called, param: 44
Return code is: 0
注意使用解引用操作符*獲取的只是最後被調用的插槽的返回值,如果需要知道每個插槽函數的返回值需要使用合並器(combiner)。
Boost的信號/槽只能在信號被觸發時,槽函數只能是同步執行,沒有像Qt那樣的異步接口。Qt異步的實現實際上是將信號push到一個隊列中,然後由統一的線程來處理信號對應的槽函數而已。當然也可以根據這個原理自己封裝帶異步的信號/槽機制,不過那樣的話應該需要另外開啟線程了。