基于字符串和基于函数指针的connect比较

本文对比了Qt5中基于字符串和基于函数指针的信号槽连接方式。基于字符串的方式在运行时进行类型检查,支持隐式类型转换和C++与QML的交互,但存在类型错误检测延迟和无法利用编译期检查的问题。而基于函数指针的方式则在编译期进行类型检查,提供更好的类型安全,但不支持QML的连接。此外,还介绍了如何处理信号参数多于槽函数参数的情况,并通过示例展示了两种方式的使用。
摘要由CSDN通过智能技术生成

关于Qt – connect的使用, 首先推荐大家两篇我之前写的两篇文章
Qt5 connect使用之“传说中的第五参数”
Qt5 connect 使用之“重载信号和槽“

从Qt5.0开始,Qt提供了两种不同的方法用来实现“信号–槽 connect”, 一种是之前版本就有的基于字符串的connect, 另一种是Qt5.0新增的基于方法指针的connect。两种不同方法各有优缺点, 这里做一个简单的总结:

基于字符串基于函数指针
类型检查时机运行时检查编译期检查
支持类型隐式转换
支持lambda表达式
信号参数个数比槽函数多的connect(使用默认参数)
C++与QML之间的connect

一、类型检查与隐式类型转换

基于字符串的connect 在运行时才会进行类型检查,这样做相对于基于函数指针的connect会有以下劣势:

  • 程序运行时才能检测到connect类型错误
  • 不支持在信号与槽函数参数的隐式转换
  • typedef 和 namespace 无法解析

因为C++无法比较字符串中的类型信息,所以基于字符串的connect依赖于字符串的精确匹配,而基于函数指针的connect可以在编译期进行类型检查,捕获类型错误,进行隐式转换,识别相同类型的不同名称

以下为两个示例:

// 1.函数类型隐式转换
QSlider *slider               = new QSlider;
QDoubleSpinBox *doubleSpinBox = new QDoubleSpinBox;

// ok. 可以正常connect成功
connect(slider, &QSlider::valueChanged, doubleSpinBox, &QDoubleSpinBox::setValue);

// error.connect失败. int类型和double类型不匹配
connect(slider, SIGNAL(valueChanged(int)), doubleSpinBox, SLOT(setValue(double)));
// 2.相同类型的不同名称
auto audioInput = new QAudioInput(QAudioFormat(), this);
auto widget = new QWidget(this);

// OK
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), widget, SLOT(show()));

// error.字符串 "State" 和 "QAudio::State"不匹配
using namespace QAudio;
connect(audioInput, SIGNAL(stateChanged(State)), widget, SLOT(show()));

二、信号参数个数比槽函数多的connect

通常,槽函数具有与信号相同(或更少)的参数并且所有参数类型兼容的情况下,才能connect成功。

但是, 基于字符串的connect 如果槽函数有默认参数,信号则可以省略这些参数。当发出信号的参数少于槽函数的参数时,Qt将使用默认参数值进行connect。

// .h 
class Test : public QWidget {
    Q_OBJECT
public:
    Test(QWidget * parent = nullptr);
public slots:
    void testSlot(int number = 42, const QString& str = "test") {
        qDebug() << number << str;
    }
};
// .cpp
Test::Test(QWidget * parent) 
{
    QPushButton *btn = new QPushButton(this);

    // error.编译失败
    connect(btn, &QPushButton::clicked, this, &Test::testSlot);
    // ok
    connect(btn, SIGNAL(clicked()), this, SLOT(testSlot()));
}

三、C++与QML之间的connect

基于字符串的connect 可以绑定C++对象QML对象之前的信号槽,但是基于函数指针的connect不可以。因为QML类型是在运行时解析的, 基于函数指针的connect无法做到这一点。

下面这个案例, 单机QML对象, C++对象会打印一条消息,反之亦然。

当C++中的QPushButton点击, 控制台会打印 QML received: “Hello from C++!”

当点击QML中的Rectangle, 控制台会打印 ''C++ received: "Hello from QML!

// QML.QmlGui.qml
Rectangle {
    width: 100; height: 100

    signal qmlSignal(string sentMsg)
    function qmlSlot(receivedMsg) {
        console.log("QML received: " + receivedMsg)
    }

    MouseArea {
        anchors.fill: parent
        onClicked: qmlSignal("Hello from QML!")
    }
}
// cpp. CppGui.cpp
class CppGui : public QWidget {
    Q_OBJECT

    QPushButton *button;

signals:
    void cppSignal(const QVariant& sentMsg) const;

public slots:
    void cppSlot(const QString& receivedMsg) const {
        qDebug() << "C++ received:" << receivedMsg;
    }

public:
    CppGui(QWidget *parent = nullptr) : QWidget(parent) {
        button = new QPushButton("Click Me!", this);
        connect(button, &QPushButton::clicked, [=] {
            emit cppSignal("Hello from C++!");
        });
    }
};

二者进行connect

auto cppObj = new CppGui(this);
auto quickWidget = new QQuickWidget(QUrl("QmlGui.qml"), this);
auto qmlObj = quickWidget->rootObject();

// Connect QML signal to C++ slot
connect(qmlObj, SIGNAL(qmlSignal(QString)), cppObj, SLOT(cppSlot(QString)));

// Connect C++ signal to QML slot
connect(cppObj, SIGNAL(cppSignal(QVariant)), qmlObj, SLOT(qmlSlot(QVariant)));

Note: QML中JavaScript中使用的 var 类型, 对应C++中的QVariant 类型。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值