在Qt5.0之后,QObject::connect在String-Based的基础上增加了Functor-Base形式的语法,他们之间的区别主要如下:
基于字符串的 | 基于函数的 | |
---|---|---|
类型检查完成时间 | 运行时 | 编译时 |
是否可以隐式转换 | 否 | 是 |
支持信号连接至Lambda | 否 | 是 |
支持槽参数大于信参数(通过默认参数) | 是 | 否 |
支持C++函数连接至QML函数 | 是 | 否 |
下面将对上述几个方面进行解释:
一、类型检查和隐式转换
String-based类型的连接将会在运行时对比字符串来完成检查,这样有三个限制:
- 只有当程序运行时才能检查到错误
- 隐式转换无法用在信号和槽
- typedef和namespace无法被解析
后两者不可行的原因是,string比较器无法获知C++真正的类型,所以需要严格的字符串匹配。
auto slider = new QSlider(this);
auto doubleSpinBox = new QDoubleSpinBox(this);
// OK: The compiler can convert an int into a double
connect(slider, &QSlider::valueChanged,
doubleSpinBox, &QDoubleSpinBox::setValue);
// ERROR: The string table doesn't contain conversion information
connect(slider, SIGNAL(valueChanged(int)),
doubleSpinBox, SLOT(setValue(double)));
二、连接Lambda表达式
class TextSender : public QWidget {
Q_OBJECT
QLineEdit *lineEdit;
QPushButton *button;
signals:
void textCompleted(const QString& text) const;//发送一个QString参数信号
public:
TextSender(QWidget *parent = nullptr);
};
TextSender::TextSender(QWidget *parent) : QWidget(parent) {
lineEdit = new QLineEdit(this);
button = new QPushButton("Send", this);
connect(button, &QPushButton::clicked, [=] {
emit textCompleted(lineEdit->text());//虽然QPushButton的clicked信号是无参的,但是通过Lambda捕获可以发送一些额外的信息,如这里的QString.
});
}
注意:虽然Functor-based类型的连接中函数指针可以是包含普通函数(包括成员函数)的任意类型,但是为了可读性,最好是slots Lambda表达式或者signals中的一种。
三、连接C++对象至QML对象
不了解QML,略
四、在slots中使用默认参数连接更少参数的信号
一般来说,信号参数和槽参数个数是相等的,当然你可以发送多点信号给我,槽可以不接收,但是一般不能少发信号。
public slots:
void printNumber(int number = 42) {
qDebug() << "Lucky number" << number;
}
DemoWidget::DemoWidget(QWidget *parent) : QWidget(parent) {
// OK: printNumber() will be called with a default value of 42
connect(qApp, SIGNAL(aboutToQuit()),
this, SLOT(printNumber()));//String-based参数通过默认实参实现匹配,OK
// ERROR: Compiler requires compatible arguments
connect(qApp, &QCoreApplication::aboutToQuit,
this, &DemoWidget::printNumber);//Functor-based要求参数是完全匹配的,Not OK
}
四、 选择重载的信号和槽
在String-based中,信号与槽参数情况是显式给定的,不存在歧义;在Function-based中,信号与槽的参数列表被省略,存在歧义。解决这个问题也比较简单,有三个方法:
static_cast<T>()
强制转换对应的信号与槽- 声明一个函数指针作为参数传入
QOverload<T>::of()
QT方式1 调用QOverload的静态方法ofqOverload<T>()
QT方式2 调用qOverload函数模板
static_cast和Qt的两种类型QOverload、qOverload的区别在于,QT的T是参数,而static_cast则是完整的函数类型。
auto slider = new QSlider(this);
auto lcd = new QLCDNumber(this);
// String-based syntax
connect(slider, SIGNAL(valueChanged(int)),
lcd, SLOT(display(int)));
// Functor-based syntax, first alternative
connect(slider, &QSlider::valueChanged,
lcd, static_cast<void (QLCDNumber::*)(int)>(&QLCDNumber::display));
// Functor-based syntax, second alternative
void (QLCDNumber::*mySlot)(int) = &QLCDNumber::display;
connect(slider, &QSlider::valueChanged,
lcd, mySlot);
// Functor-based syntax, third alternative
connect(slider, &QSlider::valueChanged,
lcd, QOverload<int>::of(&QLCDNumber::display));
// Functor-based syntax, fourth alternative (requires C++14)
connect(slider, &QSlider::valueChanged,
lcd, qOverload<int>(&QLCDNumber::display));