找回密码
 立即注册
收起左侧

Qt学习之路第16篇 标准对话框 QMessageBox

4
回复
18045
查看
[复制链接]
累计签到:3 天
连续签到:1 天
来源: 2013-9-9 13:36:38 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
版权声明

该文章原创于Qter开源社区(www.qter.org),作者devbean,博客www.devbean.net,转载请注明出处!



在前面的章节(信号槽自定义信号槽)中,我们详细介绍了有关 Qt 5 的信号槽新语法。由于这次改动很大,许多以前看起来不是问题的问题接踵而来,因此,我们用单独的一章重新介绍一些 Qt 5 的信号槽新语法。


基本用法

Qt 5 引入了信号槽的新语法:使用函数指针能够获得编译期的类型检查。使用我们在自定义信号槽中设计的 Newspaper 类,我们来看看其基本语法:

  1. //!!! Qt5
  2. #include <QObject>

  3. ////////// newspaper.h
  4. class Newspaper : public QObject
  5. {
  6.     Q_OBJECT
  7. public:
  8.     Newspaper(const QString & name) :
  9.         m_name(name)
  10.     {
  11.     }

  12.     void send() const
  13.     {
  14.         emit newPaper(m_name);
  15.     }

  16. signals:
  17.     void newPaper(const QString &name);

  18. private:
  19.     QString m_name;
  20. };

  21. ////////// reader.h
  22. #include <QObject>
  23. #include <QDebug>

  24. class Reader : public QObject
  25. {
  26.     Q_OBJECT
  27. public:
  28.     Reader() {}

  29.     void receiveNewspaper(cosnt QString & name) const
  30.     {
  31.         qDebug() << "Receives Newspaper: " << name;
  32.     }
  33. };

  34. ////////// main.cpp
  35. #include <QCoreApplication>

  36. #include "newspaper.h"
  37. #include "reader.h"

  38. int main(int argc, char *argv[])
  39. {
  40.     QCoreApplication app(argc, argv);

  41.     Newspaper newspaper("Newspaper A");
  42.     Reader reader;
  43.     QObject::connect(&newspaper, &Newspaper::newPaper,
  44.                      &reader,    &Reader::receiveNewspaper);
  45.     newspaper.send();

  46.     return app.exec();
  47. }
复制代码

在 main() 函数中,我们使用 connect() 函数将 newspaper 对象的 newPaper() 信号与 reader 对象的 receiveNewspaper() 槽函数联系起来。当 newspaper 发出这个信号时,reader 相应的槽函数就会自动被调用。这里我们使用了取址操作符,取到 Newspaper::newPaper() 信号的地址,同样类似的取到了 Reader::receiveNewspaper() 函数地址。编译器能够利用这两个地址,在编译期对这个连接操作进行检查,如果有个任何错误(包括对象没有这个信号,或者信号参数不匹配等),编译时就会发现。


有重载的信号

如果信号有重载,比如我们向 Newspaper 类增加一个新的信号:

  1. void newPaper(const QString &name, const QDate &date);
复制代码

此时如果还是按照前面的写法,编译器会报出一个错误:由于这个函数(注意,信号实际也是一个普通的函数)有重载,因此不能用一个取址操作符获取其地址。回想一下 Qt 4 中的处理。在 Qt 4 中,我们使用 SIGNAL 和 SLOT 两个宏来连接信号槽。如果有一个带有两个参数的信号,像上面那种,那么,我们就可以使用下面的代码:

  1. QObject::connect(&newspaper, SIGNAL(newPaper(QString, QDate)),
  2.                  &reader,    SLOT(receiveNewspaper(QString, QDate)));
复制代码

注意,我们临时增加了一个 receiveNewspaper() 函数的重载,以便支持两个参数的信号。在 Qt 4 中不存在我们所说的错误,因为 Qt 4 的信号槽连接是带有参数的。因此,Qt 能够自己判断究竟是哪一个信号对应了哪一个槽。



对此,我们也给出了一个解决方案,使用一个函数指针来指明到底是哪一个信号:

  1. void (Newspaper:: *newPaperNameDate)(const QString &, const QDate &) = &Newspaper::newPaper;
  2. QObject::connect(&newspaper, newPaperNameDate,
  3.                  &reader,    &Reader::receiveNewspaper);
复制代码

这样,我们使用了函数指针 newspaperNameDate 声明一个带有 QString 和 QDate 两个参数,返回值是 void 的函数,将该函数作为信号,与 Reader::receiveNewspaper() 槽连接起来。这样,我们就回避了之前编译器的错误。归根结底,这个错误是因为函数重载,编译器不知道要取哪一个函数的地址,而我们显式指明一个函数就可以了。


如果你觉得这种写法很难看,想像前面一样写成一行,当然也是由解决方法的:

  1. QObject::connect(&newspaper,
  2.                  (void (Newspaper:: *)(const QString &, const QDate &))&Newspaper::newPaper,
  3.                  &reader,
  4.                  &Reader::receiveNewspaper);
复制代码

这是一种换汤不换药的做法:我们只是声明了一个匿名的函数指针,而之前我们的函数指针是有名字的。不过,我们并不推荐这样写,而是希望以下的写法:

  1. QObject::connect(&newspaper,
  2.                  static_cast<void (Newspaper:: *)(const QString &, const QDate &)>(&Newspaper::newPaper),
  3.                  &reader,
  4.                  &Reader::receiveNewspaper);
复制代码

对比上面两种写法。第一个使用的是 C 风格的强制类型转换。此时,如果你改变了信号的类型,那么你就会有一个潜在的运行时错误。例如,如果我们把 (const QString &, const QDate &) 两个参数修改成 (const QDate &, const QString &),C 风格的强制类型转换就会失败,并且这个错误只能在运行时发现。而第二种则是 C++ 推荐的风格,当参数类型改变时,编译器会检测到这个错误。



注意,这里我们只是强调了函数参数的问题。如果前面的对象都错了呢?比如,我们写的 newspaper 对象并不是一个 Newspaper,而是 Newspaper2?此时,编译器会直接失败,因为 connect() 函数会去寻找 sender->*signal,如果这两个参数不满足,则会直接报错。



带有默认参数的槽函数

Qt 允许信号和槽的参数数目不一致:槽函数的参数数目要比信号的参数少。这是因为,我们信号的参数实际是作为一种返回值。正如普通的函数调用一样,我们可以选择忽略函数返回值,但是不能使用一个并不存在的返回值。如果槽函数的参数数目比信号的多,在槽函数中就使用到这些参数的时候,实际这些参数并不存在(因为信号的参数比槽的少,因此并没有传过来),函数就会报错。这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。比如

  1. void QPushButton::clicked(bool checked = false)
复制代码

就是这种情况。



然而,有一种情况,槽函数的参数可以比信号的多,那就是槽函数的参数带有默认值。比如,我们的 Newspaper 和 Reader 有下面的代码:

  1. // Newspaper
  2. signals:
  3.     void newPaper(const QString &name);
  4. // Reader
  5.     void receiveNewspaper(const QString &name, const QDate &date = QDate::currentDate());
复制代码

虽然 Reader::receiveNewspaper() 的参数数目比 Newspaper::newPaper() 多,但是由于 Reader::receiveNewspaper() 后面一个参数带有默认值,所以该参数不是必须提供的。但是,如果你按照前面的写法,比如如下的代码:

  1. QObject::connect(&newspaper,
  2.                  static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper),
  3.                  &reader,
  4.                  static_cast<void (Reader:: *)(const QString &, const QDate & =QDate::currentDate())>(&Reader::receiveNewspaper));
复制代码

你会得到一个断言错误:


The slot requires more arguments than the signal provides.

我们不能在函数指针中使用函数参数的默认值。这是 C++ 语言的限制:参数默认值只能使用在直接地函数调用中。当使用函数指针取其地址的时候,默认参数是不可见的!



当然,此时你可以选择 Qt 4 的连接语法。如果你还是想使用 Qt 5 的新语法,目前的办法只有一个:Lambda 表达式。不要担心你的编译器不支持 Lambda 表达式,因为在你使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。于是,我们的代码就变成了:

  1. QObject::connect(&newspaper,
  2.                  static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper),
  3.                  [=](const QString &name) { /* Your code here. */ });
复制代码









参与人数 2人气 +3 收起 理由
kazusa + 1
zhangl + 2 对我帮助很大!

查看全部评分总评分 : 人气 +3

回复

使用道具 举报

累计签到:38 天
连续签到:1 天
2014-1-20 10:47:06 显示全部楼层
您好,
Qt 允许信号和槽的参数数目不一致:槽函数的参数数目要比信号的参数少。这是因为,我们信号的参数实际是作为一种返回值。正如普通的函数调用一样,我们可以选择忽略函数返回值,但是不能使用一个并不存在的返回值。如果槽函数的参数数目比信号的多,在槽函数中就使用到这些参数的时候,实际这些参数并不存在(因为信号的参数比槽的少,因此并没有传过来),函数就会报错。这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。比如
void QPushButton::clicked(bool checked = false)
复制代码

就是这种情况。
这段话,我看了好久,还是没明白
回复 支持 1 反对 0

使用道具 举报

累计签到:17 天
连续签到:1 天
2014-8-22 16:44:12 显示全部楼层
默认参数在获取指针的时候还是当成了一个没有默认值的参数,
回复 支持 反对

使用道具 举报

累计签到:17 天
连续签到:1 天
2014-8-22 16:51:55 显示全部楼层
但是最后的Lambda表达式没看懂。。。。
回复 支持 反对

使用道具 举报

累计签到:20 天
连续签到:1 天
2016-5-28 14:54:13 显示全部楼层
Qt中也开始支持Lambda表达式,这是挺让人兴奋的地方
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了