【翻译 + 整理】Qt基于字符串和函数指针的connect连接之间的区别

一、对比 

从Qt 5开始,QT提供了两种不同的方式来编写信号槽连接:基于字符串(SIGNAL/SLOT将信号/槽转成一个字符串)的连接语法和基于函数指针的连接语法。这两种语法各有利弊。下表总结了它们的区别:

 字符串函数指针
类型检查时期:运行时编译时
可以执行隐式类型转换 
可以将信号连接到lambda表达式 
可以将信号连接到参数多于信号的槽(使用默认参数) 
可以将C++函数连接到QML函数 

二、类型检查和隐式类型转换

基于字符串的connect在运行时才进行类型检查。这有三个局限性:

  1. 只有在程序开始运行后才能检测到连接错误。
  2. 不能在信号和槽之间进行参数的隐式转换。
  3. 无法解析typedef和名称空间。

2和3的存在,是因为字符串比较器不能访问C++类型信息,因此依赖于精确字符串匹配。

相反,编译器检查基于函数指针的连接。编译器在编译时捕获错误,启用兼容类型之间的隐式转换,并识别同一类型的不同名称。

三、与Lambda表达式建立连接

基于函数指针的连接语法可以将信号连接到C++ 11 lambda表达式,这些表达式实际上是内联槽。此功能在基于字符串的语法中不可用。
注意:基于函数指针的连接语法接受指向所有函数的指针,包括独立函数和常规成员函数。但是,为了可读性,信号应该只连接到槽、lambda表达式和其他信号。

四、将C++对象连接到QML对象

基于字符串的语法可以将C++对象连接到QML对象,但是基于函数指针的语法不能。这是因为QML类型在运行时被解析,因此它们不适用于C++编译器
在下面的示例中,单击QML对象使C++对象打印消息,反之亦然。

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!")
      }
  }
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++!");
          });
      }
  };

连接信号: 

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

      // 连接QML信号到C++槽
      connect(qmlObj, SIGNAL(qmlSignal(QString)),cppObj, SLOT(cppSlot(QString)));

      // 连接C++信号到QML槽
      connect(cppObj, SIGNAL(cppSignal(QVariant)),qmlObj, SLOT(qmlSlot(QVariant)));

注:QML中所有的JavaScript函数都采用var类型的参数,此类型参数映射到C++中的QVariant类型。

五、槽中的默认参数个数 < 信号中参数个数

基于字符串的连接语法为槽中的默认参数个数 < 信号中参数个数提供了一种解决方法:如果槽具有默认参数,则可以从信号中忽略这些参数。当发出的信号参数少于槽参数时,Qt使用默认参数值运行槽。
基于函数指针的连接不支持此功能。基于函数指针的连接要解除这种限制需使用lambda表达式连接。

六、重载信号槽

基于字符串的语法,可以显式指定参数类型。因此,重载信号或槽的期望实例是明确的。
基于函数指针的语法,必须强制转换重载信号或槽,以告诉编译器要使用哪个实例。

例:QLCDNumber 有几种重载槽:

  1. QLCDNumber::display(int)
  2. QLCDNumber::display(double)
  3. QLCDNumber::display(QString)

基于函数指针的重载几种形式(connect(obj1,地址1,obj2,地址2);):

      auto slider = new QSlider(this);
      auto lcd = new QLCDNumber(this);

      connect(slider, &QSlider::valueChanged,lcd, static_cast<void (QLCDNumber::*)(int)>(&QLCDNumber::display));//函数的地址转成QLCDNumber类的函数指针可以指向的地址

      void (QLCDNumber::*mySlot)(int) = &QLCDNumber::display;//指向QLCDNumber类的函数指针 = 成员函数的地址
      connect(slider, &QSlider::valueChanged,lcd, mySlot);

      //C++14以后才支持
      connect(slider, &QSlider::valueChanged,lcd, qOverload<int>(&QLCDNumber::display));

1、auto qOverload(T functionPointer)

返回指向重载函数的指针。template参数是函数的参数类型列表。functionPointer是指向函数的指针(非成员函数也可以)

      struct Foo {
          void overloadedFunction();
          void overloadedFunction(int, const QString &);
      };
      qOverload<>(&Foo::overloadedFunction) //返回指向void overloadedFunction()的地址
      qOverload<int, const QString &>(&Foo::overloadedFunction)

2、auto qConstOverload(T memberFunctionPointer)

返回指向const成员函数的指针(后面带const的):

3、auto qNonConstOverload(T memberFunctionPointer)

返回指向非const成员函数的指针。

      struct Foo {
          void overloadedFunction(int, const QString &);        //A
          void overloadedFunction(int, const QString &) const;  //B
      };
      qConstOverload<int, const QString &>(&Foo::overloadedFunction)    //对应B
      qNonConstOverload<int, const QString &>(&Foo::overloadedFunction) //对应A
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值