信号与槽重载的解决方案
一、通过函数指针解决
//信号
void (Me::*funchungury)() = &Me::hungury;
void (Me::*funchungury_QString)(QString) = &Me::hungury;
//槽
void (Me::*funceat)() = &Me::eat;
void (Me::*funceat_QString)(QString) = &Me::eat;
//有参连接
connect(me,funchungury_QString,me,funceat_QString);
//无参连接
connect(me,funchungury,me,funceat);
继续使用自定义信号时使用的父子窗口切换的案例:
/*
SubWidget.h文件中添加一个openMainWindow(const QString&)重载信号
*/
#ifndef _SUBWIDGET_H_
#define _SUBWIDGET_H_
#include <QWidget>
#include <QPushButton>
class SubWidget : public QWidget
{
Q_OBJECT
public:
SubWidget(QWidget* parent = nullptr);
~SubWidget();
private:
QPushButton* m_btn;
signals:
void openMainWindow(); // 自定义信号
void openMainWindow(const QString&); // 带参数的自定义信号
};
#endif
/*
使用emit发送信号
*/
#include "SubWidget.h"
SubWidget::SubWidget(QWidget* parent)
:QWidget(parent)
{
resize(400, 400);
setWindowTitle("子窗口");
m_btn = new QPushButton("切换到父窗口", this);
//使用emit发送信号
connect(m_btn, &QPushButton::clicked, this, [=](){
emit openMainWindow();
emit openMainWindow(QString("我是重载的信号"));
});
}
SubWidget::~SubWidget()
{
}
/*
Widget.h文件中添加重载的槽函数funcSlot(const QString& msg);
*/
#ifndef _WIDGET_H_
#define _WIDGET_H_
#include <QWidget>
#include <QPushButton>
#include "SubWidget.h"
class SubWidget;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget* parent = nullptr);
~Widget();
public slots:
void funcSlot(); // 不带参数的槽函数
void funcSlot(const QString& msg); // 带参数的槽函数
private:
QPushButton* m_btn = nullptr;
SubWidget* m_subWidget = nullptr;
};
#endif
#include"Widget.h"
Widget::Widget(QWidget* parent)
:QWidget(parent)
{
resize(640, 480); // 设置父窗口大小
setWindowTitle("父窗口"); // 设置父窗口标题
m_btn = new QPushButton("切换到子窗口", this);
m_subWidget = new SubWidget;
//实现从父窗口切换到子窗口的效果
connect(m_btn, &QPushButton::clicked,[=]() {
this->hide();
m_subWidget->show();
});
//实现从子窗口切换到父窗口的效果
//使用函数指针的方式解决信号重载 槽重载
void (SubWidget:: * signalFunc)() = &SubWidget::openMainWindow;
void (SubWidget:: * signalFunc2)(const QString&) = &SubWidget::openMainWindow;
void (Widget:: * slotFunc)() = &Widget::funcSlot;
void (Widget:: * slotFunc2)(const QString&) = &Widget::funcSlot;
connect(m_subWidget, signalFunc, this, slotFunc); // 无参连接
//connect(m_subWidget, signalFunc2, this, slotFunc2); // 带参连接
}
Widget::~Widget()
{
}
//槽函数
void Widget::funcSlot()
{
this->show();
m_subWidget->hide();
}
//重载的带参数的槽函数
void Widget::funcSlot(const QString& msg)
{
this->show();
m_subWidget->hide();
setWindowTitle(msg);
}
上面的案例中,有参数的连接会改变父窗口的标题,没有参数的连接不会改变父窗口的标题。
不带参数的连接:
带参数的连接:
二、通过Qt提供的重载类(QOverload)解决
//有参连接
connect(this,QOverload<QString>::of(&MyButton::hungury),
this,QOverload<QString>::of(&MyButton::eat));
//无参连接
connect(this,QOverload<>::of(&MyButton::hungury),
this,QOverload<>::of(&MyButton::eat));
直接使用QOverload类的方式解决父子窗口切换信号与槽重载的问题:
//使用QOverload的方式解决信号重载 槽重载
connect(m_subWidget, QOverload<>::of(&SubWidget::openMainWindow),
this, QOverload<>::of(&Widget::funcSlot)); // 无参连接
connect(m_subWidget, QOverload<const QString&>::of(&SubWidget::openMainWindow),
this, QOverload<const QString&>::of(&Widget::funcSlot)); // 带参连接
三、使用Qt4的方式解决
这种旧的信号与槽的连接方式在Qt6中是支持的, 但是不推荐使用, 因为这种方式在进行信号槽连接的时候, 信号和槽函数通过宏
SIGNAL
和SLOT
转换为字符串类型。因为信号和槽函数的转换是通过宏来进行转换的,因此传递到宏函数内部的数据不会被进行检测, 如果使用者传错了数据,编译器也不会报错,但实际上信号槽的连接已经不对了,只有在程序运行起来之后才能发现问题,而且问题不容易被定位。
Me m;
// Qt4处理方式 注意不要把信号与槽的名字写错了,因为是转为字符串写错了不会报错,但是连接会失败
connect(&m, SIGNAL(eat()), &m, SLOT(hungury()));
connect(&m, SIGNAL(eat(QString)), &m, SLOT(hungury(QString)));
// Qt5处理方式
connect(&m, &Me::eat, &m, &Me::hungury); // error:no matching member function for call to 'connect'
直接使用Qt4的方式解决父子窗口切换信号与槽重载的问题:
//使用Qt4的方式解决信号重载 槽重载
connect(m_subWidget, SIGNAL(openMainWindow()),
this, SLOT(funcSlot()));
connect(m_subWidget, SIGNAL(openMainWindow(const QString&)),
this, SLOT(funcSlot(const QString&)));
总结
Qt4的信号槽连接方式使用了宏函数, 宏函数对用户传递的信号槽不会做错误检测, 容易出bug
Qt5的信号槽连接方式, 传递的是信号槽函数的地址, 编译器会做错误检测, 减少了bug的产生
当信号槽函数被重载之后, Qt4的信号槽连接方式不受影响,Qt6中需要给被重载的信号或者槽定义函数指针或者使用QOverload