信號槽是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到一個隊列中,然後由統一的線程來處理信號對應的槽函數而已。當然也可以根據這個原理自己封裝帶異步的信號/槽機制,不過那樣的話應該需要另外開啟線程了。