Qt 5中信号和槽的新语法

作者:Cheng Liang | Aug 7, 2012 8:48:26 AM

参考原文:woboq: Olivier Goffart - Signals and Slots in Qt5

Qt 5 Alpha已经发布。我曾经工作过的一个新特性就是信号和槽的新语法。本篇博客将会为您讲解这一内容。

Qt 5之前的语法

这里是我们如何把信号和槽连接起来的方法:

connect(sender, SIGNAL(valueChanged(QString, QString)),
receiver, SLOT(updateValue(QString)));

幕后的实际情况是SIGNALSLOT这两个宏会把它们的参数转换成字符串。然后QObject::connect()将会把这些字符串和moc工具所收集的内省(introspection)数据进行比较。

Qt 5之前信号和槽语法的问题

虽然通常情况下都可以正常工作,我们还是发现了如下问题:

  • 没有编译期检查:因为函数名被处理成字符串,所有的检查都是在运行时完成的。这就是为什么有时会发生编译通过了,但槽并没有被调用。此时,你就应该去检查标准输出,看看有没有什么警告说明这个连接没有成功。
  • 因为处理的是字符串,所以槽函数中的类型名字必须与信号的完全一致,而且在头文件中的和实际的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++11 Lambda表达式

至此之前,我们所有的示例都是基于C++98标准的。但是,如果你的编译器支持C++11,我还是强烈建议你使用一些这个语言的新特性。现在,Lambda表达式至少被MSVC 2010GCC 4.5clang 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呀。

(译者注:感谢齐亮对本篇博客全文进行了修正。)