Qt下普通成员函数和静态成员函数作为回调函数的实现(替代信号与槽)

news2025/1/26 15:34:32

文章目录

  • 前言
  • 一、使用信号与槽
  • 二、什么是回调函数
  • 三、使用普通成员函数作为回调函数
  • 四、使用静态成员函数作为回调函数
  • 五、示例完整代码
  • 总结


前言

在Qt中,使用信号与槽来实现不同对象之间的通信是非常方便的,这也是Qt框架中引以为傲的一项机制,这里对此不加以介绍,可以查看参考文章。在信号与槽之前,对象间的通信可以采用回调实现,接下来我们通过下面示例来学习如何将Qt下普通成员函数和静态成员函数作为回调函数来实现不同对象的通信,希望可以帮助到大家,如有错误之处,欢迎大家批评指正。

项目效果
请添加图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、使用信号与槽

示例主界面是Widget,在其初始化函数中创建了子界面对象CallBackAdd,并在这里进行了信号与槽的连接,下面代码中可以看到信号与槽在Qt4和Qt5的写法,实现子界面点击后将输入的两个数值相加的结果发送到主界面显示出来:

下面的initWidget()函数是在主界面Widget类的构造函数中进行了调用:

//初始化界面
void Widget::initWidget()
{
    //创建子界面对象并显示
    m_addWidget = new CallBackAdd();
    m_addWidget->move(500,400);   //移到指定坐标防止遮挡主界面
    m_addWidget->show();
    
    //使用信号与槽
    //connect(m_addWidget,SIGNAL(signal_addNum(int)),this,SLOT(slot_addNum(int)));   //Qt4写法
    connect(m_addWidget,&CallBackAdd::signal_addNum,this,&Widget::slot_addNum);    //Qt5写法
}

主界面Widget类的h文件中添加槽函数:

private slots:
    void slot_addNum(int addNum);

Widget类的cpp文件中进行定义:

//槽函数
void Widget::slot_addNum(int addNum)
{
    ui->le_num->setText(QString::number(addNum));
}

子界面CallBackAdd类的h文件中添加信号:

signals:
    void signal_addNum(int addNum);

CallBackAdd类的cpp文件中点击按钮后发送信号:

//按钮点击槽函数
void CallBackAdd::on_pb_add_clicked()
{
    int num_1 = ui->le_num_1->text().toInt();
    int num_2 = ui->le_num_2->text().toInt();
    int addNum = num_1 + num_2;

    //发送信号
    emit signal_addNum(addNum);
}

关于信号与槽的更多介绍可以查看该文章:C++ Qt开发:如何使用信号与槽

二、什么是回调函数

上面使用信号与槽实现了不同对象之间的通信,下面让我们先了解一下回调函数,再使用回调函数来替代信号与槽实现通信。

这里引用参考文章中的内容:函数指针的调用,即是一个通过函数指针调用的函数;如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,就说这是回调函数。

参考文章:什么是回调函数?为什么要使用回调函数?如何使用回调函数?

三、使用普通成员函数作为回调函数

为什么类的普通成员函数不能作为回调函数去注册?因为普通成员函数中隐含着所属类的this指针,如果将普通成员函数注册给对方,但对方使用这个函数指针时,就会发生参数列表匹配的问题。这时候我们可以通过function和bind实现将普通成员函数作为回调函数进行调用,其用法可以参考:C++11 bind和function用法

主界面Widget类的h文件中添加普通成员函数的声明:

private:
	//普通成员函数作为回调函数
	void getAddResult(int addResult);

Widget类的cpp文件中进行定义:

//初始化界面
void Widget::initWidget()
{
	...
    //使用回调函数(普通成员函数),通过function和bind实现
    m_addWidget->setCallBackFun(std::bind(&Widget::getAddResult,this,_1));
}

//回调函数的定义(普通成员函数)
void Widget::getAddResult(int addResult)
{
    ui->le_num->setText(QString::number(addResult));
}

子界面CallBackAdd类的h文件中添加相关头文件和设置回调函数的接口函数等:

#include <iostream>
#include <functional>

using namespace std;
using namespace std::placeholders;   //占位符_N所在的命名空间

//声明一个function类型的可调用对象(可理解为函数指针)
typedef std::function<void(int)> CallBack;

class CallBackAdd : public QWidget
{
	...
public:
    //设置回调函数的接口函数
    void setCallBackFun(CallBack fun);
    
private:
    CallBack m_callbackFun;
}

CallBackAdd类的h文件中进行定义:

//设置回调函数
void CallBackAdd::setCallBackFun(CallBack fun)
{
    m_callbackFun = fun;
}

//按钮点击槽函数
void CallBackAdd::on_pb_add_clicked()
{
    int num_1 = ui->le_num_1->text().toInt();
    int num_2 = ui->le_num_2->text().toInt();
    int addNum = num_1 + num_2;
    
    //使用回调
    m_callbackFun(addNum);
}

四、使用静态成员函数作为回调函数

静态成员函数不包含this指针,可以直接将静态成员函数注册给对方,但是本示例的静态成员函数中有调用ui对象,同样因为不含this指针,所以不能直接访问ui对象,这里定义一个m_widget来间接使用this指针。

主界面Widget类的h文件中添加普通成员函数的声明:

private:
	//声明一个静态的Widget类变量
	static Widget *m_widget;

	//使用static函数作为回调函数
	static void getAddNum(int addNum);

Widget类的cpp文件中进行定义:

#include "widget.h"

//类外初始化静态的Widget类变量
Widget *Widget::m_widget = nullptr;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //赋值m_widget变量
    m_widget = this;

    //初始化界面
    this->initWidget();
}

//初始化界面
void Widget::initWidget()
{
	...
    //使用回调函数(静态成员函数)
    m_addWidget->setCallBackFun(getAddNum);
}

//回调函数的定义(静态成员函数)
void Widget::getAddNum(int addNum)
{
    //静态成员函数并不能直接访问ui对象(普通成员函数)
    //此处使用m_widget变量来实现静态成员函数操作ui对象
    m_widget->ui->le_num->setText(QString::number(addNum));
}

子界面CallBackAdd类与第三部分中的代码一致。

五、示例完整代码

1.callbackadd.h

#ifndef CALLBACKADD_H
#define CALLBACKADD_H

#include <QWidget>
#include <iostream>
#include <functional>

using namespace std;
using namespace std::placeholders;   //占位符_N所在的命名空间

//声明一个function类型的可调用对象(可理解为函数指针)
typedef std::function<void(int)> CallBack;

namespace Ui {
class CallBackAdd;
}

class CallBackAdd : public QWidget
{
    Q_OBJECT

public:
    explicit CallBackAdd(QWidget *parent = nullptr);
    ~CallBackAdd();

    //设置回调函数的接口函数
    void setCallBackFun(CallBack fun);

signals:
    void signal_addNum(int addNum);

private slots:
    void on_pb_add_clicked();

private:
    Ui::CallBackAdd *ui;

    CallBack m_callbackFun;
};

#endif // CALLBACKADD_H

2.callbackadd.cpp

#include "callbackadd.h"
#include "ui_callbackadd.h"

CallBackAdd::CallBackAdd(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::CallBackAdd)
{
    ui->setupUi(this);
    this->setAttribute(Qt::WA_QuitOnClose,false);   //设置随父窗口关闭
}

CallBackAdd::~CallBackAdd()
{
    delete ui;
}

//设置回调函数
void CallBackAdd::setCallBackFun(CallBack fun)
{
    m_callbackFun = fun;
}

//按钮点击槽函数
void CallBackAdd::on_pb_add_clicked()
{
    int num_1 = ui->le_num_1->text().toInt();
    int num_2 = ui->le_num_2->text().toInt();
    int addNum = num_1 + num_2;

    //发送信号
    //emit signal_addNum(addNum);

    //使用回调
    m_callbackFun(addNum);
}

3.widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "callbackadd.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void initWidget();

private slots:
    void slot_addNum(int addNum);

private:
    Ui::Widget *ui;

    CallBackAdd *m_addWidget;

private:
    //普通成员函数作为回调函数
    void getAddResult(int addResult);

    //声明一个静态的Widget类变量
    static Widget *m_widget;

    //使用static函数作为回调函数
    static void getAddNum(int addNum);

};
#endif // WIDGET_H

4.widget.cpp

#include "widget.h"
#include "ui_widget.h"

//类外初始化静态的Widget类变量
Widget *Widget::m_widget = nullptr;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //赋值m_widget变量
    m_widget = this;

    //初始化界面
    this->initWidget();
}

Widget::~Widget()
{
    delete ui;
}

//初始化界面
void Widget::initWidget()
{
    //创建子界面对象并显示
    m_addWidget = new CallBackAdd();
    m_addWidget->move(500,400);   //移到指定坐标防止遮挡主界面
    m_addWidget->show();

    //使用信号与槽
    //connect(m_addWidget,SIGNAL(signal_addNum(int)),this,SLOT(slot_addNum(int)));   //Qt4写法
    //connect(m_addWidget,&CallBackAdd::signal_addNum,this,&Widget::slot_addNum);   //Qt5写法

    //使用回调函数(普通成员函数),通过function和bind实现
    //m_addWidget->setCallBackFun(std::bind(&Widget::getAddResult,this,_1));

    //使用回调函数(静态成员函数)
    m_addWidget->setCallBackFun(getAddNum);
}

//槽函数
void Widget::slot_addNum(int addNum)
{
    ui->le_num->setText(QString::number(addNum));
}

//回调函数的定义(普通成员函数)
void Widget::getAddResult(int addResult)
{
    ui->le_num->setText(QString::number(addResult));
}

//回调函数的定义(静态成员函数)
void Widget::getAddNum(int addNum)
{
    //静态成员函数并不能直接访问ui对象(普通成员函数)
    //此处使用m_widget变量来实现静态成员函数操作ui对象
    m_widget->ui->le_num->setText(QString::number(addNum));
}

5.callbackadd.ui
请添加图片描述

6.widget.ui
请添加图片描述


总结

通过以上这个示例可以看到在Qt中使用使用回调函数也是可以实现不同对象间的通信,这里的示例比较简单,主要让大家对回调函数有个简单的认识,实现回调函数还有其它的方式,详细的内容可以看一下参考文章。(在Qt下开发,信号与槽是真香啊~~)


hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。

参考文章:
C++ Qt开发:如何使用信号与槽
什么是回调函数?为什么要使用回调函数?如何使用回调函数?
C++11 bind和function用法
五种将c++类的成员函数用作回调函数的方法——史上最全、最简!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1329848.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自然语言处理(NLP):理解语言,赋能未来

目录 前言1 什么是NLP2 NLP的用途3 发展历史4 NLP的基本任务4.1 词性标注&#xff08;Part-of-Speech Tagging&#xff09;4.2 命名实体识别&#xff08;Named Entity Recognition&#xff09;4.3 共指消解&#xff08;Co-reference Resolution&#xff09;4.4 依存关系分析&am…

Redis设计与实现之AOF

一、AOF Redis 分别提供了 RDB 和 AOF 两种持久化机制: RDB 将数据库的快照(snapshot)以二进制的方式保存到磁盘中。 AOF 则以协议文本的方式&#xff0c;将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件&#xff0c;以此达到记录数据库状态的目的。 本章首先介绍…

web架构师编辑器内容-创建业务组件和编辑器基本行为

编辑器主要分为三部分&#xff0c;左侧是组件模板库&#xff0c;中间是画布区域&#xff0c;右侧是面板设置区域。 左侧是预设各种组件模板进行添加 中间是使用交互手段来更新元素的值 右侧是使用表单的方式来更新元素的值。 大致效果&#xff1a; 左侧组件模板库 最初的模板…

2023.12.21 关于 Redis 常用数据结构 和 单线程模型

目录 各数据结构具体编码方式 查看 key 对应 value 的编码方式 Reids 单线程模型 经典面试题 IO 多路复用 Redis 常用数据结构 Redis 中所有的 key 均为 String 类型&#xff0c;而不同的是 value 的数据类型却有很多种以下介绍 5 种 value 常见的数据类型 注意&#xff1…

下一站,上岸@24考研er

时间过的好快&#xff0c; 考研倒计时①天 去年这个时候&#xff0c; 我应该也是充满未知地进入即将来到的考研初试 去年&#xff0c;这个时候&#xff0c;疫情&#x1f637;刚刚放开 许多人都&#x1f411;&#xff0c;发烧&#xff0c;可幸的是我受影响不大 &#x1f3…

itk中的配准整理

文章目录 Perform 2D Translation Registration With Mean Squares效果:源码: 多模态互信息配准 Perform Multi Modality Registration With Viola Wells Mutual Information效果图源码: Register Image to Another Using Landmarks 通过标记点配准图像效果图源码 Perform 2D T…

关键字:import关键字

在 Java 中&#xff0c;import关键字用于导入类或接口&#xff0c;使你可以在代码中使用它们而无需完全限定其名称。以下是使用import关键字的示例代码&#xff1a; 在上述示例中&#xff0c;通过使用import关键字导入了java.util.ArrayList类&#xff0c;这样就可以在代码中直…

代码图形注释自动生成(通过文字图像)

0. 简介 大家在学&#xff08;CTRL&#xff09;习&#xff08;C&#xff09;别人代码的时候&#xff0c;看到别人的代码程序&#xff0c;在日志中有很多很酷的代码注释&#xff0c;或者是有一些图形化注释方便理解。之前本人以为都是一个个手敲出来的。然后在网上一番搜索&…

80x86汇编—寻址方式

文章目录 术语解释8086寻址方式直数寻址寄存器间接寻址寄存器相对寻址基址变址寻址比例变址寻址方式基址比例变址寻址方式 术语解释 EA&#xff1a;有效地址&#xff0c;通过段地址&#xff1a;偏移地址组合得到的Effect Address 位移量&#xff1a;一般是常量和标号&#xff…

HTTP前端请求

目录 HTTP 请求1.请求组成2.请求方式与数据格式get 请求示例post 请求示例json 请求示例multipart 请求示例数据格式小结 3.表单3.1.作用与语法3.2.常见的表单项 4.session 原理5.jwt 原理 HTTP 请求 1.请求组成 请求由三部分组成 请求行请求头请求体 可以用 telnet 程序测…

解决xcode 运行不老iPhone 15 iOS 17.1 设备的问题

问题 最近要查看一下ios 17.1的设备的性能&#xff0c;但是当前版本的Xcode运行不了 解决方法 1、更新Xcode版本到15.1以上 2、更新完成后&#xff0c;大概率出现这个情况 原因&#xff1a;在app Store中更新到Xcode15后,运行不了模拟器和真机.需要下载iOS 17对应的模拟器.&…

力扣每日一题day38[106. 从中序与后序遍历序列构造二叉树]

给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7], postorder [9,15,7,20,3] 输出&#xff1a;[…

Java 面试 多线程遇到的问题,如何处理

问题描述 某服务在运行过程中抛出了 RejectedExecutionException。 配置参数&#xff1a; corepoolsize 50, maxpoolsize 50&#xff0c; workqueue 为 SynchronousQueue 现象&#xff1a; 当新的task被拒绝时&#xff0c;pool size未达到配置值50. Caused by: java.util.conc…

dotnet命令创建C#项目,VSCode打开

在命令行中创建项目并运行 1.首先安装.net 下载地址:.NET | 构建。测试。部署。 2.在 cmd 控制台输入 dotnet --vesion 检查版本号是否正常 3.我用git bash环境输入命令创建项目 // 创建文件夹 mkdir MyVSCode // 进入该文件夹 cd MyVSCode/ // 创建控制台项目 dotnet …

java类和对象的思想概述

0.面向对象Object OOP——名人名言&#xff1a;类是写出来的&#xff0c;对象是new出来的 **> 学习面向对象的三条路线 java类以及类成员&#xff1a;&#xff08;重点&#xff09;类成员——属性、方法、构造器、&#xff08;熟悉&#xff09;代码块、内部类面向对象特征&…

【音视频】Mesh、Mcu、SFU三种框架的总结

目录 三种网络场景介绍 【Mesh】 【MCU】(MultiPoint Control Unit) 【SFU】(Selective Forwarding Unit) 三种网络架构的优缺点 Mesh架构 MCU架构(MultiPoint Control Unit) SFU架构(Selective Forwarding Unit) 总结 参考文章 三种网络场景介绍 【Mesh】 Mesh架构…

<软考高项备考>《论文专题 - 24 整合管理(2) 》

3 过程2-制订项目管理计划 3.1 问题 4W1H过程1-制定项目章程做什么定义、准备和协调项目计划的所有组成部分&#xff0c;并把它们整合为一份综合项目管理计划的过程&#xff1b;作用&#xff1a;生成一份综合文件&#xff0c;用于确定所有项目工作的基础及其执行方式为什么做…

大创项目推荐 深度学习+python+opencv实现动物识别 - 图像识别

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 inception_v3网络5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; *…

Uniapp 开发 BLE

BLE 低功耗蓝牙&#xff08;Bluetooth Low Energy&#xff0c;或称Bluetooth LE、BLE&#xff0c;旧商标Bluetooth Smart&#xff09;&#xff0c;用于医疗保健、运动健身、安防、工业控制、家庭娱乐等领域。在如今的物联网时代下大放异彩&#xff0c;扮演者重要一环&#xff…

互联网账户一证通查询名下账号

核验身份后一键在线查询名下所有关联号码以及注册。 名下电话卡查询&#xff1a;全国移动电话卡“一证通查” 手机号绑定查询&#xff1a;https://tb3.cn/A3lhMk