从Qt5.0开始,Qt提供了两种不同的方式进行信号槽的连接:基于 字符串 的连接语法、基于 函数 的连接语法。这两种语法各有利弊,下面对它们的不同点进行总结。

以下几部分详细解释了它们之间的不同,并说明如何使用它们,以及各自的优点。

一、类型检查以及隐式类型转换

基于字符串的连接是在运行时通过字符串比较来进行类型检查,这种方式有3个局限性:

1.只有在程序运行后才能查出连接错误;

2.信号和槽之间不能进行隐式转换;

3.类型定义和名字空间不能被识别。

第2第3个局限存在的原因是,字符串的比较并不会涉及C++的类型信息,因此它严格依赖于字符串的匹配。

相反地,基于函数的连接由编译器进行检查,编译器在编译时捕捉错误,并且允许相容类型的隐式转换,而且它能识别相同类型的不同名称。

例如,信号有一个int类型的参数,而槽函数接受一个double类型的参数,只有基于函数的语法能够将他们连接起来。QSlider有一个int值,而QDubleSpinBox有一个double值,下面的代码段说明了如何将二者保持同步:

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)));

下面的例子说明了名称识别的不足。

QAudioInput::stateChanged()声明时带有一个“QAudio::State”类型的参数,因此,基于字符串的连接也必须识别“QAudio::Satae”,即使“State”已经是可见的。这个特性并不适用于基于函数的连接,因为参数类型不是连接的一部分。

auto audioInput = new QAudioInput(QAudioFormat(), this);
auto widget = new QWidget(this); // OK
connect(audioInput, SIGNAL(stateChanged(QAudio::State)),
widget, SLOT(show())); // ERROR: The strings "State" and "QAudio::State" don't match
using namespace QAudio;
connect(audioInput, SIGNAL(stateChanged(State)),
widget, SLOT(show())); // ...

二、使用Lambda表达式进行连接

基于函数的连接语法可以将信号和C++11中的lambda表达式(实际上是内联的槽函数)连接起来,这个特性不适用与基于字符串的语法。

下面的例子中,类TextSender发出信号textCompleted(),该信号有一个QString类型的参数,下面是这个类的定义:

class TextSender : public QWidget {
Q_OBJECT QLineEdit *lineEdit;
QPushButton *button; signals:
void textCompleted(const QString& text) const; public:
TextSender(QWidget *parent = nullptr);
};

下面是信号槽的连接,点击按钮时发出信号TextSender::textCompleted():

TextSender::TextSender(QWidget *parent) : QWidget(parent) {
lineEdit = new QLineEdit(this);
button = new QPushButton("Send", this); connect(button, &QPushButton::clicked, [=] {
emit textCompleted(lineEdit->text());
}); // ...
}

在这个例子中,lambda函数使得连接变得很简单,即使QPushButton::clicked()和TextSender::textCompleted()有不匹配的参数。相反地,如果使用基于字符串的实现方式,则需要额外的代码。

注意:基于函数的连接语法接受所有函数的指针,包括普通的非成员函数以及成员函数,但为了可读性,信号应该只和槽、lambda表达式和其它信号连接。

三、连接C++对象和QML对象

基于字符串的语法可以连接C++对象和QML对象,但是基于函数的语法无法连接。因为QML类型在运行时识别,它们并不适用于C++编译器。

四、在槽中使用默认参数,与参数更少的信号进行连接

通常,只有在槽的参数小于等于信号的参数时,并且所有参数相匹配时,连接才能建立。

基于字符串的连接语法为这个规则提供了一个变通方案:当信号发出并且它的参数比槽的参数少时,如果槽有默认参数,这些参数可以从信号中省略,Qt使用默认值调用槽函数。

基于函数的连接不支持这种特性。

假如有一个叫做DemoWidget的类,槽函数printNumber()有一个默认参数:

public slots:
void printNumber(int number = 42) {
qDebug() << "Lucky number" << number;
}

使用基于字符串的连接,DemoWidget::printNumber()可以被连接到QApplication::aboutToQuit(),即使后者有更少的参数。基于函数的连接会在编译时报错:

DemoWidget::DemoWidget(QWidget *parent) : QWidget(parent) {

	// OK: printNumber() will be called with a default value of 42
connect(qApp, SIGNAL(aboutToQuit()),
this, SLOT(printNumber())); // ERROR: Compiler requires compatible arguments
connect(qApp, &QApplication::aboutToQuit,
this, &DemoWidget::printNumber);
}

五、选择重载信号或者槽

使用基于字符串的语法,参数类型是显式确定的。最后,使用哪个重载信号或槽是不明确的。

相反地,使用基于函数的语法,重载的信号或槽必须被强制转换,告诉编译器使用哪个。

例如,QSignalMapper有4种形式的mapped()信号:

1.QSignalMapper::mapped(int)
2.QSignalMapper::mapped(QString)
3.QSignalMapper::mapped(QWidget*)
4.QSignalMapper::mapped(QObject*)

为了连接int版本的QSpinBox::setValue(),两种语法写法如下:

auto mapper = new QSignalMapper(this);
auto spinBox = new QSpinBox(this); // String-based syntax
connect(mapper, SIGNAL(mapped(int)),
spinBox, SLOT(setValue(int))); // Functor-based syntax, first alternative
connect(mapper, static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped),
spinBox, &QSpinBox::setValue); // Functor-based syntax, second alternative
void (QSignalMapper::*mySignal)(int) = &QSignalMapper::mapped;
connect(mapper, mySignal,
spinBox, &QSpinBox::setValue);

Qt——信号槽连接:基于字符串与基于函数的连接之间的不同的更多相关文章

  1. Qt信号槽-原理分析

    目录 一.问题 二.Moc 1.变量 2.Q_OBJECT展开后的函数声明 3.自定义信号 三.connect 四.信号触发 1.直连 2.队列连接 五.总结 六.推荐阅读 一.问题 学习Qt有一段时 ...

  2. Qt信号槽的一些事(第一次知道信号还有返回值,以及Qt::UniqueConnection)

    注:此文是站在Qt5的角度说的,对于Qt4部分是不适用的. 1.先说Qt信号槽的几种连接方式和执行方式. 1)Qt信号槽给出了五种连接方式: Qt::AutoConnection 0 自动连接:默认的 ...

  3. Qt信号槽的一些事 Qt::带返回值的信号发射方式

    一般来说,我们发出信号使用emit这个关键字来操作,但是会发现,emit并不算一个调用,所以它没有返回值.那么如果我们发出这个信号想获取一个返回值怎么办呢? 两个办法:1.通过出参形式返回,引用或者指 ...

  4. Qt信号槽的一些事

    注:此文是站在Qt5的角度说的,对于Qt4部分是不适用的. 1.先说Qt信号槽的几种连接方式和执行方式. 1)Qt信号槽给出了五种连接方式: Qt::AutoConnection 0 自动连接:默认的 ...

  5. Qt信号槽源码剖析(一)

    大家好,我是IT文艺男,来自一线大厂的一线程序员 大家在使用Qt开发程序时,都知道怎么使用Qt的信号槽,但是Qt信号槽是怎么工作的? 大部分人仍然不知道:也就是说大家只知道怎么使用,却不知道基于什么原 ...

  6. QT 信号槽connect中解决自定义数据类型或数组作为函数参数的问题——QT qRegisterMetaType 注册MetaType——关键:注册自定义数据类型或QMap等容器类

    一般情况下信号槽直接连接方式不会出现问题,但是如果信号与槽在不同线程或Qt::QueuedConnection方式连接,可能会在连接期间报以下类似问题,如: QObject::connect: Can ...

  7. QT信号槽详解

    1         QT信号槽详解 1.1  信号和槽的定义 信号是触发信号,例如按钮的点击触发一个clicked信号,槽是用来接收信号,并处理信号,相当于信号响应函数.一个信号可以关联多个槽函数,信 ...

  8. QT源码之Qt信号槽机制与事件机制的联系

    QT源码之Qt信号槽机制与事件机制的联系是本文要介绍的内容,通过解决一个问题,从中分析出的理论,先来看内容. 本文就是来解决一个问题,就是当signal和slot的连接为Qt::QueuedConne ...

  9. Qt信号槽源码剖析(二)

    大家好,我是IT文艺男,来自一线大厂的一线程序员 上节视频给大家讲解了Qt信号槽的基本概念.元对象编译器.示例代码以及Qt宏:今天接着深入分析,进入Qt信号槽源码剖析系列的第二节视频. Qt信号槽的宏 ...

  10. (文字版)Qt信号槽源码剖析(三)

    大家好,我是IT文艺男,来自一线大厂的一线程序员 上节视频给大家讲解了Qt信号槽的Qt宏展开推导:今天接着深入分析,进入Qt信号槽源码剖析系列的第三节视频. Qt信号槽宏推导归纳 #define si ...

随机推荐

  1. C语言-12-日期和时间处理标准库详细解析及示例

    概述 标准库 提供了用于日期和时间处理的结构和函数 是C++语言日期和时间处理的基础 与时间相关的类型 clock_t,本质是:unsigned long typedef unsigned long ...

  2. IOS之资源收集--很好的github网址

    1.Nick Jensen 2.关于直播的

  3. UVa 112 - Tree Summing(树的各路径求和,递归)

    题目来源:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=3&pa ...

  4. DP大作战—组合背包

    题目描述 组合背包:有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包). DD大牛的伪代码 for i = 1 to N if 第i件物品属 ...

  5. android intent 5.1

    1.intent 6 items action, data(uri &type),Component name,Extras,flags 2.data---uri & type 不管使 ...

  6. IOS沙盒Files目录说明和常用操作

    Ios沙盒目录清单 1 Documents 用于存储用户生成的文件.其他数据及其他程序不能重新创建的文件,默认文件通过iCloud自动备份.如果不需要iCloud备份,则设置标记 NSURLIsExc ...

  7. Android精品开源整理

    一.兼容类库 ActionBarSherlock : Action Bar是Android 3.0后才开始支持的,ActionBarSherlock是让Action Bar功能支持2.X后的所有平台, ...

  8. eclipse中基于maven构建的web项目pom.xml中指定的jar包无法发布到tomcat中

    eclipse运行maven web项目报错: 信息: Starting Servlet Engine: Apache Tomcat/7.0.57 一月 07, 2015 11:50:44 下午 or ...

  9. UVA 10827 Maximum sum on a torus (LA)

    算法入门经典训练指南88页练习 ::这道题只要把原矩阵扩大4倍,那么其跟最大子矩阵的题目就很类似,把二维转化成一维,求最大的序列和,不过这个序列的长度不能超过n. 长度不能超过n? 那这道题又跟hdu ...

  10. Linux 环境下如何使 Chrome 浏览器字体更漂亮

    Windows 就免谈了,本身字体渲染技术 Cleartype 以及 DirectWrite 就稀烂得一塌糊涂.Mac 下面本来字体渲染就很好,所以关键就是在 Linux 下如何使 Chrome 的字 ...