从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. NSArray,NSMutableArray的三种排序

    int main(int argc, const char * argv[]) { @autoreleasepool { //字符串进行排序 NSArray *arr=@[@"b" ...

  2. iOS Xcode7上真机调试

    在Xcode7上进行真机调试,不需要证书,步骤如下: 1. 2. 3. 4. 5.添加Apple ID后会显示两个Free,表示可以免费真机调试iOS应用和Mac应用,选中高亮选项,点击view de ...

  3. IOS开发之网络编程--文件压缩和解压缩

    前言: QQ表情包就用到了解压缩,从网络下载的那么多表情文件格式并不是一个一个图片文件,而是多个图片压缩而成的表情压缩包.下面介绍的是iOS开发中会用到的压缩和解压缩的第三方框架的使用. 注意: 这个 ...

  4. 安装Yeoman

    先安装nodejs,我用的centos7,所以可以安装5的版本,如果不是请参考https://nodejs.org/en/download/package-manager/#enterprise-li ...

  5. SAM4E单片机之旅——15、触屏输入与SPI通信

    开发板上配了一个电阻触摸屏,它的控制器是ADS7843,使用SPI进行通信.这次实现的功能是通过SPI接口与该控制器交互,获取触摸屏点击的坐标,并显示在LCD上.略为难点的是SPI作为同步时钟的一种, ...

  6. Effective Java 14 In public classes, use accessor methods, not public fields

    Principle To offer the benefits of encapsulation you should always expose private field with public ...

  7. Dell PowerVault TL4000 磁带机卡带问题

    最近一段时间Dell PowerVault TL4000 磁带机故障频繁,昨天我在管理系统里面看到Library Status告警:HE: sled blocked, error during sle ...

  8. Sql Server 高频,高并发访问中的键查找死锁解析

    死锁对于DBA或是数据库开发人员而言并不陌生,它的引发多种多样,一般而言,数据库应用的开发者在设计时都会有一定的考量进而尽量避免死锁的产生.但有时因为一些特殊应用场景如高频查询,高并发查询下由于数据库 ...

  9. 斐波那契数列 递归 尾递归 递推 C++实现

    ==================================声明================================== 本文原创,转载请注明作者和出处,并保证文章的完整性(包括本 ...

  10. jenkins忘记管理密码处理方式

    进入/var/lib/jenkins/  用vim编辑 config.xml文件 找到useSecurity部分 将true改成false      并且删除下面的 授权部分 保存推出 3.从新进入 ...