环境:win10 +qt 5.15.2
问题记录:
第一种connect连接方式:使用SIGNAL和SLOT宏
.h文件
QTimer *timSend;// 定时发送-定时器
.cpp文件
timSend = new QTimer(this);
connect(timSend, SIGNAL(timeout()), this, SLOT(obo_send_msg()));
现象,无法连接到槽函数,在工程中我的obo_send_msg();是串口发送函数,在实际调试过程消息发不出。.h不变换成换成下面的connect的写法问题解决。
这里,SIGNAL(timeout())
表示 timSend
对象发出的名为 timeout
的信号。SLOT(obo_send_msg())
则指定了接收这个信号的槽函数 obo_send_msg()
,它是在 this
(通常是指当前对象)。
第二种connect连接方式:使用&类名::函数名
刚开始我是这样写的; connect(timSend, &QTimer::timeout, this, &Widget::obo_send_msg());
报错:
error: lvalue required as unary '&' operand 错误:左值需要作为一元'&'操作数
Cannot take the address of an rvalue of type 'void' 不能接受“void”类型右值的地址。
QT初学者问题cannot take the address of an rvalue of type 'void'_编程语言-CSDN问答
参考这个回答将槽函数改成此代码:
connect(timSend, &QTimer::timeout, this, &Widget::obo_send_msg);
问题解决消息发出,-那也就是说明我的obo_send_msg函数本身是没有问题的,我也参考了同事的代码发现写法跟我是一样的,他的可以我的不行,那么一定有什么地方是我忽略了的?两种写法也存在区别?还有为什么省略了()就可以?下面一一解答:
总结:
1.第一种如果没有指定 SLOT(obo_send_msg())
的参数列表(如 SLOT(())
),可能会导致连接失败,尤其是在 obo_send_msg()
需要参数的情况下。第二种写法简化了无参槽函数的连接过程,如果 obo_send_msg()
是无参的,那么直接写 obo_send_msg
就可以了。
2.省略()其实这是一个简化的形式,当省略了括号时,Qt会假设你想要连接一个不带参数的槽函数。这意味着即使 obo_send_msg()
原型声明了参数,这里也会尝试匹配一个无参的版本。如果你的 obo_send_msg()
实际上不需要参数,那么这就会正常地将信号和槽连接起来。
3.第二种写法是QT5中加入的一种重载形式,指定信号和槽两个参数不再使用SIGNAL()和 SLOT()宏,并且槽函数不再必须是使用slots关键字声明的函数,可以是任意能和信号关联的成员函数。要使一个成员函数可以和信号关联,那么这个函数的参数数目不能超过信号的参数数目,但是并不要求该函数拥有的参数类型和信号中对应的参数类型完全一致,只需要可以进行隐式转换即可。
看到这句话一下点醒了我,查看obo_send_msg();声明果然定义在了private:中没有定义在private slots:中。这里也告诉我我们槽函数一定要定义在槽里,不要像我一样犯低级错误。
找到了问题,那就来找两种槽连接的区别和优缺点。结论就不必说了在我声明错误的情况下 使用&类名::函数名的方式都能准确地连接到目标函数,可见其准确性。
在Qt中,当你试图使用connect()
函数将信号(SIGNAL)与槽(SLOT)关联起来时,如果直接使用SLOT()
语法(如this, SLOT(obo_send_msg())
),并且信号不是来自QObject
的基类(比如在这里的QTimer
),那么可能会出现连接失败的情况。
QTimer::timeout
是一个特定于QTimer
的信号,而obo_send_msg
是你自定义类中的槽函数。当使用第一种写法时,SIGNAL(timeout())
期望的是一个从继承了QObject
的类派生出来的对象发出的timeout
信号。由于QTimer
本身就具有这个信号,所以你应该直接引用它的信号名称加上括号表示方法,而不是用SLOT()
。
第二种写法connect(timSend, &QTimer::timeout, this, &Widget::obo_send_msg)
则是正确的,因为它明确指定了信号的成员函数地址(&QTimer::timeout
)和接收者的对象以及槽函数(&Widget::obo_send_msg
)。这种形式无论信号是否来自QObject
的基类,都能准确地连接到目标函数。
connect(timSend, &QTimer::timeout, this, &YourClass::obo_send_msg);
总结一下:其中YourClass
是包含obo_send_msg
槽函数的那个类名。如果你不确定信号是从哪个基类继承来的,通常直接使用这种方式连接会更保险。
以下为网友总结
1、不需要写参数更简便。
2、不需要槽函数的参数类型与信号对应的参数类型完全一致,只需要进行隐藏式转换。
3、可以在编译时进行检查,比如信号或槽函数的拼写错误、槽函数参数数量多于信号的参数数量等都能在编译时期发现,而不是运行时。
就如一些qt的书所言:建议在编写Qt5的代码时使用&类名::函数名的connect方式。