参考原文:woboq: Olivier Goffart - Signals and Slots in Qt5
Qt 5 Alpha已经发布。我曾经工作过的一个新特性就是信号和槽的新语法。本篇博客将会为您讲解这一内容。
这里是我们如何把信号和槽连接起来的方法:
connect(sender, SIGNAL(valueChanged(QString, QString)),
receiver, SLOT(updateValue(QString)));
幕后的实际情况是SIGNAL
和SLOT
这两个宏会把它们的参数转换成字符串。然后QObject::connect()
将会把这些字符串和moc
工具所收集的内省(introspection)数据进行比较。
虽然通常情况下都可以正常工作,我们还是发现了如下问题:
connect()
语句中的也必须一致。也就是说,如果你使用了typedef
或者命名空间,那么这个连接就不能正常工作。在即将到来的Qt 5中提供了一套新的语法。之前的语法依然可以使用,但是现在,我们有了全新的方式:
connect(sender, &Sender::valueChanged,
receiver, &Receiver::updateValue);
哪一种方式更酷可能取决于个人喜好。但大家会更快的适应新方式。
现在让我们先远离美学视角,来看看新语法有什么好处:
如果信号或者槽的名字的拼写发生了错误,或者槽函数的参数与信号的不一致,你会在编译期就得到一个错误。
这肯定会在重构、或者修改信号或槽函数的名字时节省很多时间。
另一个好处是,如果我们少了Q_OBJECT
宏,可以利用static_cast
返回更友好的错误信息。
现在,我们不仅可以更好地使用typedef
或者命名空间,而且可以利用隐式类型转换。
在下面的例子中,我们的信号有一个QString
参数,而槽函数需要的是QVariant
。它可以正常工作,是因为QVariant
有一个可以使用QString
的隐式构造函数。
class Test : public QObject
{
Q_OBJECT
public:
Test() {
connect(this, &Test::someSignal, this, &Test::someSlot);
}
signals:
void someSignal(const QString &);
public:
void someSlot(const QVariant &);
};
如果你留心上面的例子,就会发现,我们的信号被连接到了一个槽,但是它的声明只有public
,没有slot
。Qt的新语法通过函数指针直接调用函数,而不再需要moc
的内省(但是信号依然需要)。
更进一步,我们可以将信号连接到任意函数或者函数对象(functor):
static void someFunction() {
qDebug() << "pressed";
}// ... 然后其它地方
QObject::connect(button, &QPushButton::clicked, someFunction);
这样处理,就可以让你很方便的同boost
或者tr1::bind
进行协作。
至此之前,我们所有的示例都是基于C++98
标准的。但是,如果你的编译器支持C++11
,我还是强烈建议你使用一些这个语言的新特性。现在,Lambda
表达式至少被MSVC 2010
、GCC 4.5
、clang 3.1
这几个编译器支持。不过对于后面两个编译器,你需要在编译时加上-std=c++0x
参数。
然后我们就可以这样写代码了:
void MyWindow::saveDocumentAs() {
QFileDialog *dlg = new QFileDialog();
dlg->open();
QObject::connect(dlg, &QDialog::finished, [=](int result) {
if (result) {
QFile file(dlg->selectedFiles().first());
// ... 在这里保存文档 ...
}
dlg->deleteLater();
});
}
这种语法允许我们更方便地编写异步代码。
更新:您还可以看看Qt 5中提供的其它C++11特性。
让我们来尝试一下吧。下载Qt 5 Alpha,开始玩吧。不要忘记报告Bug呀。
(译者注:感谢齐亮对本篇博客全文进行了修正。)