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

2023-12-23 08:35:48


前言

在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++类的成员函数用作回调函数的方法——史上最全、最简!!!

文章来源:https://blog.csdn.net/XCJandLL/article/details/135125398
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。