最近在看google talk的libjingle模块,里面大量使用了signal/slot机制,下面就这个机制说明下自己的理解。
所谓signal,即信号;
所谓slot,即信号触发后所要处理的事情(所要调用的函数)。
之所以称其为slot(槽),我这么理解:当一个signal被触发后,有可能处理多个事件(函数),将signal想象为一张表(信号-处理函数),那么每个处理函数便是表中的一个槽。
我们基于一个简单的场景来分析这个机制:
有一个对话框,上面有两个按钮(称b1与b2),并有一个文本标签(称label),欲实现如下功能:
1、点击b1,lable显示“点击了b1”
2、点击b2, lable显示“点击了b2”
b1的类的定义如下
class cb1
{
public:
void on_b1_click();
signal0<> Click; // signal对象
}
b2的类的定义如下
class cb2
{
public:
void on_b2_click();
signal0<> Click; // signal对象
}
对话框类定义如下
class dlg
{
void set_from_b1() { edit.setwindowtext(“点击了b1”)}
void set_from_b2() { edit.setwindowtext(“点击了b2”)}
}
在对话框的初始化函数内,有如下调用
b1.Click.connect(this, set_from_b1); // 将set_from_b1函数注册给b1.Click信号
b2.Click.connect(this, set_from_b2); // 将set_from_b2函数注册给b2.Click信号
b1被按下时的响应函数
void cb1::on_b1_click()
{
// 执行其对应的信号处理函数
// 在本例中,为set_from_b1
Click.emit();
}
b2被按下时的响应函数
void cb2:on_b2_click()
{
// 执行其对应的信号处理函数
// 在本例中,为set_from_b2
Click.emit();
}
通过以上的伪代码,我们实现了这两个功能,并且,class cb1,cb2完全独立,可实现复用。
代码分析:
以上代码的关键之处在于两个地方:
1、事件处理函数的注册
观察class signal0的内部实现,原理是,将参数传入的类对象指针与其函数指针,打包存入一个容器。待后续调用。
2、事件处理函数的执行
注册后,当执行emit时,遍历signal0对象内部的容器,取出函数指针一一调用即可。