本文讲解 Qt 信号槽的三种连接形式,Qt 的信号槽最初来源于函数回调,但注册回调函数有一定局限,安全性也没有保证。所以一定程度上可以说信号槽是对回调机制进行了封装。
Qt 的信号槽能够匹配上,必须要满足两个条件
- 信号的参数个数大于等于槽函数
- 等于部分的参数类型必须一一匹配
1. 使用 ui 界面控件
第一种,使用 ui 界面上的控件,通过右键 -> 转到槽,则会在对应界面的 cpp 生成类似以下代码的函数体
void MainWindow::on_pushButton_clicked()
{
}
此种方式生成槽函数,编译时不进行检查,在运行时连接,通过 Qt 自身 moc (meta object compiler) 系统的反射机制来连接两个函数,所以在控件名称修改时,在运行时会提示连接失败。
另一个这种连接方式也不容易维护,连接和解除连接都不在可控范围内。
2. Qt4 的连接语法
第二种方式,使用 Qt4 语法的连接,也就是使用宏扩展, 本质上还是利用字符串的反射机制,示例:
connect(sender, SIGNAL(sigfunc()), receiver, SLOT((slotfunc()));
如果查看 SIGNAL, SLOT 这两个宏的实现,就能发下这两个宏是将函数转换为字符串,但相较第一种方式,此种方式编译阶段做了字符串形式的参数一致性检查。
缺点是无法确认类中是否包含此函数,可以在两个宏中放入两个参数匹配但根本不存在的函数,一样能在编译期间顺利通过编译,却在运行时提示连接失败。
3. Qt5 连接语法
第三种,Qt5 中提供了函数指针形式的 connect 语法,示例:
connect(sender, &Sender::signal, receiver, &Receiver::slot);
类名加函数取地址,确保了编译器检查信号与槽函数是否匹配,可以减少运行时出现连接失败的情况。
还有一种 lambda 表达式的变体,也是使用函数指针的方式来连接
connect(sender, &Sender::signal, [](){
//... implement of slot
});
但 Qt5 语法中如果出现信号或槽函数或两者都有重载的情况下,直接使用会编译报错:
no matching member function for call to 'connect'
可以使用 Qt 的 QOverload 来处理,假如有以下信号和槽函数
signals:
void sigfunc(int);
void sigfunc(QString);
//...
public slots:
void slotfunc(int);
void slotfunc(QString);
连接时可以使用如下方式:
connect(sender, QOverload<int>::of(&Sender::sigfunc), receiver, QOverload<int>::of(&Receiver::slotfunc));
和
connect(sender, QOverload<QString>::of(&Sender::sigfunc), receiver, QOverload<QString>::of(&Receiver::slotfunc));
来分别连接信号和槽函数的重载