关于Qt C++中connect的几种写法代码示例

 更新时间:2024年11月20日 11:58:07   作者:Winrich  
这篇文章介绍了Qt中connect函数的不同编写方式,包括传统的槽函数写法、使用函数指针的写法、Lambda表达式以及使用QOverload选择重载信号的写法,每种写法都有其特点和适用场景,程序员应根据具体需求选择最合适的方式,需要的朋友可以参考下

前言

connect函数就像是编程世界里的“茴”字,千变万化,各有千秋。咱们程序员呢,就像是孔乙己那样,虽然有时候会觉得这些变化有些好笑,甚至有些令人头疼,但说到底,还不是得乖乖地学着、用着,毕竟这可是编程里的“必备技能”。connect的编写每个人都有自己的习惯,也有其特点,还是要深究一下的。

在Qt中,connect函数用于将信号(signal)与槽(slot)连接起来,以便在信号被发射时自动调用槽函数。Qt提供了几种不同的connect写法,以及定义槽函数的方式,它们各有特点和适用场景。

1. 传统的槽函数写法

在传统的Qt项目中,槽函数通常是在类的头文件中使用slots:关键字声明的特殊成员函数。例如:

class MainWindow : public QMainWindow {
  Q_OBJECT
 
public:
  MainWindow(QWidget *parent = nullptr);
  ~MainWindow();
 
private slots: // 使用slots关键字声明槽函数
  void onCalculateClicked();
 
  // ... 其他成员和槽函数 ...
};

然后在源文件中实现这个槽函数:

void MainWindow::onCalculateClicked() {
  // 槽函数的实现
}

使用这种写法时,conect函数通常这样调用:

connect(pbt, SIGNAL(clicked()), this, SLOT(onCalculateClicked()));

这里使用的是SIGNAL()SLOT()宏来将信号和槽转换为字符串形式的标识符。

2. 使用函数指针的connect写法(5.0)

在5.0及后续的Qt版本中,推荐使用函数指针的connect写法,因为它提供了更好的类型检查和编译时错误检测。这种写法不需要使用slots:关键字或SIGNAL()/SLOT()宏,而是直接使用函数指针。例如:

connect(pbt, &QPushButton::clicked, this, &MainWindow::onCalculateClicked);

在这种写法中,&QPushButton::clicked是信号clicked的函数指针,&MainWindow::onCalculateClicked是槽函数的函数指针。编译器会在编译时检查这些指针是否匹配。这种写法的优点如下:

  • 类型安全
    函数指针的connect写法提供了编译时的类型检查。这意味着如果信号和槽的函数签名不匹配,或者指定的函数不存在,编译器会立即给出错误提示。这大大减少了运行时错误的可能性,因为所有的连接都是在编译时验证的。

  • 易于阅读和维护
    使用函数指针使得代码更加直观和易于理解。你可以直接看到哪个信号被连接到了哪个槽,而不需要通过字符串来间接表示。这有助于代码的维护和调试,因为你可以更容易地跟踪信号和槽之间的连接关系。

  • 自动处理函数重载
    当存在多个重载的信号或槽函数时,使用函数指针可以自动选择正确的重载版本。传统的SIGNAL()SLOT()宏在处理重载函数时可能会遇到问题,因为它们依赖于函数名的字符串表示,无法区分重载的函数。而函数指针则包含了函数的完整签名,因此可以准确地选择所需的函数。

  • 提高性能
    虽然性能差异可能不是非常显著,但使用函数指针的connect写法可能比使用字符串的宏稍微快一些。这是因为函数指针可以直接在编译时解析和连接,而不需要在运行时进行字符串比较和查找。

  • 与现代C++标准兼容
    随着C++标准的不断发展,使用函数指针的connect写法更符合现代C++的编程风格。它利用了C++11及更高版本中的特性,如lambda表达式和智能指针,使得Qt的代码可以与这些现代特性更好地集成。

  • 减少宏的使用
    减少宏的使用可以降低代码的复杂性和潜在的错误。宏在C++中经常是导致难以调试的问题的源头,因为它们是在预处理阶段展开的,而不是在编译阶段进行类型检查的。使用函数指针可以避免这些问题。

3. Lambda表达式作为槽函数(C++11)

在C++11以后,除了传统的槽函数和函数指针,Qt还支持使用Lambda表达式作为槽函数。这种写法非常灵活,因为它允许你在connect调用中直接编写槽函数的代码,而不需要在类中定义额外的成员函数。例如:

connect(pbt, &QPushButton::clicked, this, [this]() {
  // Lambda表达式作为槽函数的实现
  bool isOK;
  double r = lEdit->text().toDouble(&isOK);
  if (isOK && r >= 0) {
    lab2->setText(QString::number(AreaCircal(r)));
  } else {
    lab2->setText("请输入合法的半径!");
  }
});

这种写法的优点如下:

  • 捕获或引用捕获的方式来控制这些变量的生命周期和作用域。

  • 灵活性和动态性
    Lambda表达式提供了一种灵活的方式来定义槽函数的行为。你可以在connect调用中直接编写Lambda表达式,而不需要事先在类中声明槽函数。这使得你可以根据需要在运行时动态地定义槽函数的行为。

  • 减少代码量
    使用Lambda表达式作为槽函数可以减少需要编写的代码量。你不需要为每个槽函数都声明一个成员函数,这不仅可以减少代码量,还可以使代码更加紧凑和易于管理。

  • 类型安全
    与传统的字符串宏相比,使用Lambda表达式作为槽函数提供了更好的类型安全。编译器会在编译时检查Lambda表达式的类型是否与信号的参数类型匹配,从而避免了运行时错误。

除了第一种传统写法外,现在较为流行的二、三种写法对比分析如下:

特性Lambda表达式函数指针
引入时间C++11C++早期版本、Qt5.0
定义方式匿名函数,直接在代码中定义需要显式声明和定义函数,然后通过函数指针指向该函数
语法简洁性简洁,直接在调用处定义相对繁琐,需要额外的函数声明和定义
类型推断支持,编译器可根据上下文推断类型不支持,需要显式指定函数返回类型和参数类型
捕获外部变量可以捕获外部作用域中的变量无法直接捕获外部变量,只能访问函数参数
执行环境在新的栈帧中执行,具有独立的调用环境和栈空间指向已存在的函数,不拥有独立的栈空间
灵活性适用于简单的、单行表达式的场合,易于在需要的时间和地点实现功能闭包适用于调用已经定义好的函数,支持动态绑定和回调函数
代码重用性通常用于一次性或短暂的函数定义,代码重用性较低可以通过函数指针在不同位置调用同一函数,代码重用性较高
类型安全性类型安全,编译器检查函数签名类型安全性较低,容易出现类型不匹配的问题
性能现代编译器优化后性能损失可忽略,但在某些情况下可能带来轻微开销通常性能开销较低,但间接调用可能带来一些额外开销
使用场景适用于需要定义简单匿名函数的场景,如STL算法中的排序和过滤适用于需要动态调用不同函数或实现回调函数的场景,如事件处理、插件系统等

上述三种方法的特点和区别总结:

  • 传统槽函数:易于阅读和维护,特别是在槽函数逻辑复杂或需要多次重用的情况下。但是,它们增加了类的复杂度,因为需要在头文件中声明槽函数。
  • 函数指针的connect:提供了更好的类型安全性,减少了运行时错误的可能性。它是现代Qt编程的推荐方式。
  • Lambda表达式:非常灵活,适用于简单的、一次性的槽函数逻辑。但是,如果Lambda表达式过于复杂,可能会降低代码的可读性。

4.使用QOverload选择重载信号的写法(Qt5.7)

使用QOverload选择重载信号的写法同样是在Qt5引入的,在Qt框架中具有显著的优点,并且适用于特定的场景。例如使用comboBox时,有时使用索引,有时使用字符串作为参数:

    QComboBox *comboBox = new QComboBox;
    QLabel *label = new QLabel;
    label->setText("初始文本");
 
    // 添加一些选项到组合框
    comboBox->addItem("选项 1");
    comboBox->addItem("选项 2");
    comboBox->addItem("选项 3");

    // 使用 QOverload 来连接重载的信号到不同的槽
    QObject::connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
                     [](int index){ qDebug() << "当前索引改变:" << index; });
 
    QObject::connect(comboBox, QOverload<const QString&>::of(&QComboBox::currentIndexChanged),
                     [](const QString &text){ qDebug() << "当前文本改变:" << text; });

在这个例子中,currentIndexChanged信号被重载了两次:一次接受一个整型参数(表示当前选中项的索引),另一次接受一个字符串参数(表示当前选中项的文本)。我们使用QOverload模板类来指定我们想要连接的是哪一个重载版本的信号。然后,我们使用lambda表达式来定义当信号发出时执行的代码。QOverload是Qt 5.7及更高版本中引入的一个模板类,它用于在连接重载信号时提供类型安全。在Qt 5.7之前的版本中,您可能需要使用static_cast或函数指针来实现相同的功能,但这样做通常不太安全且容易出错。

具体优点:

  • 类型安全
    • 使用QOverload可以确保在连接信号和槽时,信号和槽的参数类型完全匹配。这有助于在编译时捕获潜在的错误,提高代码的稳定性。
  • 清晰明确
    • 通过显式地指定要连接的重载信号版本,代码更加清晰易懂。其他开发者可以一目了然地看到信号和槽之间的对应关系,减少误解和错误。
  • 灵活性
    • QOverload允许开发者在信号存在多个重载版本的情况下,选择最适合当前需求的信号版本进行连接。这增加了代码的灵活性,使其能够适应不同的场景和需求。
  • 避免歧义
    • 在没有QOverload之前,如果信号存在多个重载版本,开发者需要通过其他方式(如函数指针转换)来指定要连接的信号版本,这可能会引入歧义和错误。而QOverload提供了一种直接且明确的方式来避免这些问题。

这种写法用于当信号存在多个重载版本时,通过QOverload模板类明确指定要连接的是哪一个重载版本的信号。比如comboBox::currentIndexChanged信号可能有多个重载版本,而通过使用QOverload<int>或QOverload<const QString&>明确指定,然后进行处理。适应场景:

  • 信号存在多个重载版本
    • 当类中的信号存在多个重载版本时,使用QOverload可以明确指定要连接的重载信号版本,从而避免连接错误或歧义。
  • 需要类型安全的信号槽连接
    • 在对类型安全要求较高的项目中,使用QOverload可以确保信号和槽的参数类型完全匹配,减少运行时错误的可能性。
  • 复杂的GUI应用程序
    • 在复杂的GUI应用程序中,信号和槽的连接可能变得非常复杂。使用QOverload可以使代码更加清晰易懂,方便维护和管理。
  • 需要灵活处理不同参数类型的场景
    • 在某些场景下,可能需要根据不同的参数类型来处理信号。使用QOverload可以方便地选择不同参数类型的信号版本进行连接,从而满足这些需求。

写到这里,感觉有点与孔乙己说的茴字的几种写法行为相仿,这里Qt里的connect函数,简直就是编程界的“茴”字啊!孔乙己要是穿越到编程世界,看到connect的种种写法,怕是要笑得胡子都抖掉了,心里还得嘀咕:“这信号槽的connect,怎地也有如此多般变化,真是绝了!”

想象一下,咱们这connect函数,简直就是编程里的“百搭小能手”,既能跟老式的函数指针眉来眼去,又能跟新潮的Lambda表达式暗送秋波,还能跟那信号映射机制搞点小暧昧。这不,就像孔乙己研究“茴”字的几种写法一样,咱们程序员也得琢磨琢磨connect的几种姿势-_-||b

在选择哪种写法时,应根据具体的需求和代码风格来决定。对于复杂的槽函数逻辑,传统的槽函数可能更合适;对于简单的逻辑或临时的连接,Lambda表达式可能更方便;而函数指针的connect则通常是一个折中的选择,既安全又易于使用。

总结

到此这篇关于关于Qt C++中connect的几种写法的文章就介绍到这了,更多相关Qt C++中connect写法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中new和delete匹配使用过程详解

    C++中new和delete匹配使用过程详解

    关于 new 和 delete 的使用相信大家并不陌生,可是为什么使用 new 的时候要用 delete,使用 new[] 的时候又要用 delete[]呢?本文就来和大家详细说说
    2023-02-02
  • opencv实现图像颜色空间转换

    opencv实现图像颜色空间转换

    这篇文章主要为大家详细介绍了opencv实现图像颜色空间转换,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • C++工厂方法之对象创建型模式详解

    C++工厂方法之对象创建型模式详解

    这篇文章主要为大家详细介绍了C++对象创建型模式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • 利用boost获取时间并格式化的方法

    利用boost获取时间并格式化的方法

    下面小编就为大家带来一篇利用boost获取时间并格式化的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • C语言实现经典扫雷小游戏完整代码(递归展开 + 选择标记)

    C语言实现经典扫雷小游戏完整代码(递归展开 + 选择标记)

    这篇文章主要介绍了C语言小项目之扫雷游戏带递归展开 + 选择标记效果,本代码中,我们用字符 ! 来标识雷,文中附有完整代码,需要的朋友可以参考下
    2022-05-05
  • C++模拟实现stack和Queue的操作示例

    C++模拟实现stack和Queue的操作示例

    这篇文章主要介绍了C++模拟实现stack和Queue的操作示例,文中通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-06-06
  • C++实现日期类的方法详解

    C++实现日期类的方法详解

    这篇文章主要给大家介绍了C++实现日期类的方法,文中通过代码示例给大家介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-01-01
  • C语言一篇精通链表的各种操作

    C语言一篇精通链表的各种操作

    链表是一种常见的重要的数据结构。它是动态地进行存储分配的一种结构,是根据需要开辟内存单元,链表这种数据结构,必须利用指针变量才能实现,即一个结点中应包含一个指针变量,用它存放下一结点的地址
    2022-04-04
  • 基于C语言打造高效通讯录的示例代码

    基于C语言打造高效通讯录的示例代码

    本文主要介绍了如何使用C语言实现一个通讯录。实现通讯录的过程中,会大量用到C语言的知识点,包括但不限于:函数、自定义类型、指针、动态内存管理、文件操作,感兴趣的可以了解一下
    2023-05-05
  • C++实现将输入复制到输出的方法

    C++实现将输入复制到输出的方法

    这篇文章主要介绍了C++实现将输入复制到输出的方法,实例分析了C++字符串转换及输入输出操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07

最新评论