首页 Qt 学习之路 2 Qt 学习之路 2(16):深入 Qt5 信号槽新语法

Qt 学习之路 2(16):深入 Qt5 信号槽新语法

59 5.3K

在前面的章节(信号槽自定义信号槽)中,我们详细介绍了有关 Qt 5 的信号槽新语法。由于这次改动很大,许多以前看起来不是问题的问题接踵而来,因此,我们用单独的一章重新介绍一些 Qt 5 的信号槽新语法。

基本用法

Qt 5 引入了信号槽的新语法:使用函数指针能够获得编译期的类型检查。使用我们在自定义信号槽中设计的Newspaper类,我们来看看其基本语法:

//!!! Qt5
#include <QObject>

////////// newspaper.h
class Newspaper : public QObject
{
    Q_OBJECT
public:
    Newspaper(const QString & name) :
        m_name(name)
    {
    }

    void send() const
    {
        emit newPaper(m_name);
    }

signals:
    void newPaper(const QString &name) const;

private:
    QString m_name;
};

////////// reader.h
#include <QObject>
#include <QDebug>

class Reader : public QObject
{
    Q_OBJECT
public:
    Reader() {}

    void receiveNewspaper(const QString & name) const
    {
        qDebug() << "Receives Newspaper: " << name;
    }
};

////////// main.cpp
#include <QCoreApplication>

#include "newspaper.h"
#include "reader.h"

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    Newspaper newspaper("Newspaper A");
    Reader reader;
    QObject::connect(&newspaper, &Newspaper::newPaper,
                     &reader,    &Reader::receiveNewspaper);
    newspaper.send();

    return app.exec();
}

main()函数中,我们使用connect()函数将newspaper对象的newPaper()信号与reader对象的receiveNewspaper()槽函数联系起来。当newspaper发出这个信号时,reader相应的槽函数就会自动被调用。这里我们使用了取址操作符,取到Newspaper::newPaper()信号的地址,同样类似的取到了Reader::receiveNewspaper()函数地址。编译器能够利用这两个地址,在编译期对这个连接操作进行检查,如果有个任何错误(包括对象没有这个信号,或者信号参数不匹配等),编译时就会发现。

有重载的信号

如果信号有重载,比如我们向Newspaper类增加一个新的信号:

void newPaper(const QString &name, const QDate &date);

此时如果还是按照前面的写法,编译器会报出一个错误:由于这个函数(注意,信号实际也是一个普通的函数)有重载,因此不能用一个取址操作符获取其地址。回想一下 Qt 4 中的处理。在 Qt 4 中,我们使用SIGNALSLOT两个宏来连接信号槽。如果有一个带有两个参数的信号,像上面那种,那么,我们就可以使用下面的代码:

QObject::connect(&newspaper, SIGNAL(newPaper(QString, QDate)),
                 &reader,    SLOT(receiveNewspaper(QString, QDate)));

注意,我们临时增加了一个receiveNewspaper()函数的重载,以便支持两个参数的信号。在 Qt 4 中不存在我们所说的错误,因为 Qt 4 的信号槽连接是带有参数的。因此,Qt 能够自己判断究竟是哪一个信号对应了哪一个槽。

对此,我们也给出了一个解决方案,使用一个函数指针来指明到底是哪一个信号:

void (Newspaper:: *newPaperNameDate)(const QString &, const QDate &) = &Newspaper::newPaper;
QObject::connect(&newspaper, newPaperNameDate,
                 &reader,    &Reader::receiveNewspaper);

这样,我们使用了函数指针newspaperNameDate声明一个带有QStringQDate两个参数,返回值是 void 的函数,将该函数作为信号,与Reader::receiveNewspaper()槽连接起来。这样,我们就回避了之前编译器的错误。归根结底,这个错误是因为函数重载,编译器不知道要取哪一个函数的地址,而我们显式指明一个函数就可以了。

如果你觉得这种写法很难看,想像前面一样写成一行,当然也是由解决方法的:

QObject::connect(&newspaper,
                 (void (Newspaper:: *)(const QString &, const QDate &))&Newspaper::newPaper,
                 &reader,
                 &Reader::receiveNewspaper);

这是一种换汤不换药的做法:我们只是声明了一个匿名的函数指针,而之前我们的函数指针是有名字的。不过,我们并不推荐这样写,而是希望以下的写法:

QObject::connect(&newspaper,
                 static_cast<void (Newspaper:: *)(const QString &, const QDate &)>(&Newspaper::newPaper),
                 &reader,
                 &Reader::receiveNewspaper);

对比上面两种写法。第一个使用的是 C 风格的强制类型转换。此时,如果你改变了信号的类型,那么你就会有一个潜在的运行时错误。例如,如果我们把(const QString &, const QDate &)两个参数修改成(const QDate &, const QString &),C 风格的强制类型转换就会失败,并且这个错误只能在运行时发现。而第二种则是 C++ 推荐的风格,当参数类型改变时,编译器会检测到这个错误。

注意,这里我们只是强调了函数参数的问题。如果前面的对象都错了呢?比如,我们写的newspaper对象并不是一个Newspaper,而是Newspaper2?此时,编译器会直接失败,因为connect()函数会去寻找sender->*signal,如果这两个参数不满足,则会直接报错。

带有默认参数的槽函数

Qt 允许信号和槽的参数数目不一致:槽函数的参数数目可以比信号的参数少。这是因为,我们信号的参数实际是作为一种返回值。正如普通的函数调用一样,我们可以选择忽略函数返回值,但是不能使用一个并不存在的返回值。如果槽函数的参数数目比信号的多,在槽函数中就使用到这些参数的时候,实际这些参数并不存在(因为信号的参数比槽的少,因此并没有传过来),函数就会报错。这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。比如

void QPushButton::clicked(bool checked = false)

就是这种情况。

然而,有一种情况,槽函数的参数可以比信号的多,那就是槽函数的参数带有默认值。比如,我们的NewspaperReader有下面的代码:

// Newspaper
signals:
    void newPaper(const QString &name);
// Reader
    void receiveNewspaper(const QString &name, const QDate &date = QDate::currentDate());

虽然Reader::receiveNewspaper()的参数数目比Newspaper::newPaper()多,但是由于Reader::receiveNewspaper()后面一个参数带有默认值,所以该参数不是必须提供的。但是,如果你按照前面的写法,比如如下的代码:

QObject::connect(&newspaper,
                 static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper),
                 &reader,
                 static_cast<void (Reader:: *)(const QString &, const QDate & =QDate::currentDate())>(&Reader::receiveNewspaper));

你会得到一个断言错误:

The slot requires more arguments than the signal provides.

我们不能在函数指针中使用函数参数的默认值。这是 C++ 语言的限制:参数默认值只能使用在直接地函数调用中。当使用函数指针取其地址的时候,默认参数是不可见的!

当然,此时你可以选择 Qt 4 的连接语法。如果你还是想使用 Qt 5 的新语法,目前的办法只有一个:Lambda 表达式。不要担心你的编译器不支持 Lambda 表达式,因为在你使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。于是,我们的代码就变成了:

QObject::connect(&newspaper,
                 static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper),
                 [=](const QString &name) { /* Your code here. */ });

59 评论

羊八井 2012年11月17日 - 16:26

newPaperNameDate这个函数指针。
QString和QDate两个参数签名应该是const引用吧?

void (Newspaper:: *newPaperNameDate)(const QString &, const QDate &) = &Newspaper::newPaper;

回复
DevBean 2012年11月17日 - 20:31

是的,已经修改,感谢指出!

回复
smile 2013年1月13日 - 00:31

“这种情况往往有两个原因:一是信号的参数就是比槽的少,此时我们可以像前面那种写法直接连接。两外一个原因是,信号的参数带有默认值。”
应该是 “一是槽的参数就是比信号的少” 才对吧?

回复
豆子 2013年1月13日 - 09:33

感谢指出!已经修改过了~

回复
Zaccur 2016年1月21日 - 14:40

没理解, 槽的参数不就是可以比信号的少吗?所以造成错误的原因 不就是槽的参数比信号的多(话句话说就是,信号的参数比槽的少。)为什么要改?

回复
Qingyun 2016年12月5日 - 15:12

我也纳闷呢

回复
轨迹 2013年5月29日 - 16:38

写的真的很好。考虑到细节了。曾经想,既然旧式的SIGNAL,SLOT能够完成事情,怎么会多这么多connect的重载方式呢?深入去看看才知道,譬如,用函数指针的方式,让问题暴露在编译期间

回复
付强 2013年12月2日 - 15:51

有个问题,是不是QT 4不支持 信号与槽函数中有缺省参数?也不支持 函数指针作为信号与槽的参数?还有就是,static_cast不如dynamic_cast安全。对含有二义性的指针,dynamic_cast会转换失败,而static_cast却直接且粗暴地进行转换。是不是用dynamic_cast更好?

回复
豆子 2013年12月2日 - 16:09

不支持。Qt4 的信号槽本质上是字符串匹配,在处理时会忽略参数默认值。函数指针语法是 Qt5 增加的,Qt4 同样不支持。至于 static_cast 和 dynamic_cast,二者应用于不同的场景,前者提供类似 C 的强制转换,后者是子类向父类的类型转换,由于二者应用场景不同,所以也不宜说哪个更安全。的确,dynamic_cast 会“安全”一些,但很多场景并不适用。

回复
付强 2013年12月2日 - 16:43

谢谢耐心解答。

回复
2013年12月9日 - 19:36

第一个例子有一些问题:
1.“reader.h”中,

void receiveNewspaper(cosnt QString & name) const

有一个const拼错了;

2.修正问题1后,编译还是会出错:

newspaper.h: In member function ‘void Newspaper::send() const’:
newspaper.h:15:29: error: passing ‘const Newspaper’ as ‘this’ argument of ‘void Newspaper::newPaper(const QString&)’ discards qualifiers [-fpermissive]
emit newPaper(m_name);

我采取的办法是在“newspaper.h”中,将

void newPaper(const QString &name) ;

修改为

void newPaper(const QString &name) const;

这样程序就可以正常编译运行了。

不过我对这里其实没有明白。从错误信息来看,我觉得对象“newspaper”是const类型,因为只有const类型的member function才能被const类型的对象调用,所以先前编译器会报“discards qualifiers”的错误。但是newspaper为什么是const类型呢?望豆先生解惑,谢谢!

回复
豆子 2013年12月10日 - 10:08

谢谢指出!信号 newPaper() 也需要增加 const 修饰才可以。因为信号实际也是一个普通的函数,所以也必须符合 C++ 语法规定。

至于错误信息,由于 this 指针在 C++ 函数调用时隐含传入,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数,也就是这个 this。在 const 函数中,对象本身被标记为 const 的(不可改变,说“标记”可能不准确,只是这样打比方),所以指向本身的 this 指针类型也就是 const Newspaper。

回复
2013年12月10日 - 13:08

有点费解,不过大概明白一点了。
假设类LOO有一个成员函数foo(),"void foo() const"可以被理解为“void foo(const LOO *this)”,所以错误信息中的this指针类型是“const Newspaper”。

我困惑的地方是在send()函数中:

void send() const
{
emit newPaper(m_name);
}

我知道send()中的“this”类型为“const Newspaper”,那么send()函数体中newPaper()函数隐式的this,是"const Newspaper"还是"Newspaper"类型呢?我个人的理解是send()把它自己"const Newspaper"类型的this传递给了Newpaper(),但是Newpaper()的this是"Newspaper"类型,遂有“discards qualifiers ”的错误。不知道这样想对不对。

另外,上面的错误,可不可以归结为“const类型成员函数不能调用非const类型成员函数”?

很抱歉提出了一些语言特性的问题,豆子不要生气呀~

回复
豆子 2013年12月10日 - 14:11

是的,signal 本质上就是普通函数,所以问题也就是 const 函数不能调用非 const 函数。

回复
michael 2013年12月18日 - 16:08

最后的使用lambda表达式 [=](const QString &name) { /* Your code here. */ } 中,只有一个参数,而不是像之前那样提供默认参数,那岂不是和之前只有一个参数的receiveNewspaper一样?

回复
michael 2013年12月18日 - 16:11

这样,如果我的槽接口有一大段的业务需要处理,code比较多,lambda这里书写就不方便了。还有[=]获取所有外部变量只是你在这里举例随手一些还是确实需要?

回复
豆子 2013年12月18日 - 16:58

是的,Lambda 表达式在语句很多的时候看起来并不直观,那就直接用函数指针足够了。至于 [=],只是为了举个例子,究竟需不需要捕获外部变量,还是要求具体需求决定。

回复
江小白 2016年1月16日 - 20:53

豆大,最后使用的lambda表达式中,岂不是不能调用reader的成员函数,那这样的意义是什么?

回复
豆子 2016年1月17日 - 12:07

lambda 表达式可以调用成员函数,不大清楚你说的什么意思?

Zaccurli 2016年1月25日 - 14:27

不好意思,[=]我添加了,忘打了而已。我发现问题了,忘记在pro文件中添加CONFIG+=c++11。十分感谢的你的解答。

Zaccurli 2016年1月25日 - 14:30

话说为什么reader一定得是在堆上创建的呢,那这样的话当程序结束的时候 这部分内存岂不没有被释放掉?

豆子 2016年1月27日 - 12:53

这里只是用来演示用的,实际应用中应该考虑内存的问题

Zaccur 2016年1月21日 - 15:00

不好意思,不是很懂Lambda表达式,请问对应例子中/* your code */中应该是什么内容呢?如何调用Reader的成员函数呢,[ ](const QString &name){ Reader::receiveNewspaper(name, data )}; ?

回复
江小白 2016年1月21日 - 23:38

lambda表达式可以写成员函数啊,你接收的变量是const QString &name,那你引用的也应该只有这个变量啊。date这个参数是默认参数,你可以不填。
里面写成reader->receiveNewspaper(name); 即可。
如果你不是new一个Reader对象,而是声明局部变量的话,这里会报错(不存在Reader的对象)。
应该就是这样啦,描述的不准确,还请豆大指正!

江小白 2016年1月21日 - 23:43

还有,你这里突然使用一个类的成员函数,谁晓得你是那个对象的函数啊。你看清了,这里的connect只有3个参数,lambda变量是接收前一个信号的const QString &参数。这个知识点,豆大在第4节信号槽里已经给大家讲清楚了。建议你不要学Qt学的那么快,没事回头多看看c++ primer,就能理解了!

Zaccur 2016年1月22日 - 14:46

不好意思C++掌握的还不是很熟悉,但[](const QString &name){ reader->receiveNewspaper(name); }并不可以,总提示无法捕获reader,我已经是在堆上new的reader了,不清楚有什么问题。
还有就是,如果想利用默认值的话,在函数体内直接传入默认值由此来代替函数指针,比如调用receiveNewspaper(name,“somethine”);

江小白 2016年1月22日 - 15:11

贴一下你的原码吧,main函数里的内容。我觉得我几乎已经把答案告诉你了。

江小白 2016年1月22日 - 15:19

发现问题了,你的[ ]里不加=,当然捕获不了啊,加上就可以了。
底下是MSDN关于C++中Lambda表达式的介绍,看了就懂了,用法很简单。
https://msdn.microsoft.com/zh-cn/library/dd293608.aspx?f=255&MSPPError=-2147217396

QCloudy 2014年3月12日 - 20:21

static_cast(&Newspaper::newPaper)
转换的目标函数指针后面要加上 const 才能编译通过(信号声明为 const,这里要保持一致)

回复
王先先 2015年9月16日 - 10:56

此时如果还是按照前面的写法,编译器会报出一个错误:由于这个函数(注意,信号实际也是一个普通的函数)有重载,因此不能用一个取址操作符获取其地址。
=================================

豆子大神,为什么我的可以?重载信号后,编译不会报错

回复
豆子 2015年9月16日 - 12:58

不知道你用的什么编译器?是相同的代码吗?

回复
善良超哥哥 2015年12月3日 - 10:19

“槽函数的参数数目要比信号的参数少”改成“槽函数的参数数目可以比信号的参数少”是不是更贴切?

回复
豆子 2015年12月6日 - 19:45

感谢指出!

回复
loudis 2015年12月17日 - 11:22

碰到这种情况,在qt5下依旧用SIGNAL和SLOT两个宏不是更简洁吗,如果用了 有什么坏处?

回复
豆子 2015年12月20日 - 12:00

没有什么坏处,只是个人习惯问题。继续使用宏也是可以的,只是没有了编译期类型检查,这是一个见仁见智的选择。

回复
12 2016年1月16日 - 16:11

另外一个原因是,信号的参数带有默认值。比如
void QPushButton::clicked(bool checked = false)

你说槽函数的参数可以比信号参数少,我想问上面例子中的信号就算有默认值又怎样?难道槽函数可以不带任何参数吗?不带任何参数还怎么接受信号?

回复
豆子 2016年1月17日 - 12:11

槽函数当然可以没有任何参数,空参数列表的函数也可以作为槽函数。比如 QWidget::close()、QWidget::show() 都是不带有参数的。这类槽函数就是在信号发出时调用,相当于信号是一个触发的通知。

回复
12 2016年1月17日 - 13:55

嗯,你说的这点我理解。
不过我的意思是上面的例子中如果void QPushButton::clicked(bool checked = false)这里的checked是true,没有参数值的槽函数又怎么知道checked是true还是false呢?换句话说,就算checked是true,不接受任何参数的槽函数能知道是true吗?

回复
豆子 2016年1月17日 - 23:43

信号不传入参数,槽函数是不知道的;就是看是不是需要这个参数。

回复
12 2016年1月18日 - 10:33

那么下面这段话这样写就不对了:
这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。
既然信号的参数带不带有默认值和信号和槽函数的参数没有关系,第二个原因就不能算作原因

连宁 2016年1月21日 - 09:23

求一种动态添加空间的方法

回复
豆子 2016年1月25日 - 09:48

动态添加空间是什么意思?

回复
ycr 2016年3月9日 - 14:36

对于static_cast和C风格强转编译器是否会报错, 这个要看编译器的实现的吧, 一般C++编译器中的C风格强转和static_cast的功能应该是一样的啊; 这只是风格问题, 不存在很确定的区别吧?

回复
豆子 2016年3月10日 - 22:02

static_cast 和 C 风格的转换有一些差别,并不是完全一致的。例如,char c = 10; int *p = (int*)&c;这样的语句在 C 风格转换完全允许,但是有很大的隐患(注意,这里的语句是将一个 int 指针指向了一个 char 变量,因为 int 是 4 个字节,char 只有一个字节,当你给 p 赋值时,很可能会覆盖掉内存中的数据)。但是,int *q = static_cast(&c);则会有一个编译器错误。在一定程度上,static_cast 会在编译期检查指针类型的转换是否相符。由于这里我们就是转换的函数指针,因此使用 static_cast 会更合理一些。另外,基于软件工程的角度(static_cast 更具有可读性),也应该选择使用 static_cast 转换。

回复
ycr 2016年3月14日 - 11:36

恩, 测试了一下果然你是对的, 多谢~
回头看了看书, 还是很多看过就忘了 - -!! "另外, static_cast不能从表达式中去除const属性", 看来C++是保留了古老的C风格转换的特性...(之前还以为只是风格不同...)

还有个问题哈:
你的例子里有个:
QObject::connect(&newspaper,
static_cast(&Newspaper::newPaper),
&reader,
static_cast(&Reader::receiveNewspaper));

static_cast的尖括号转换符中貌似是不能用默认参数的:
error: default arguments are only permitted for function parameters
或许是我们用的编译器实现不同??

回复
豆子 2016年3月14日 - 12:37

转换 const 需要使用专门的 const_cast 才可以。带有默认参数的转换就是不可以的,在文中最末也有相关描述。

回复
罗伊马斯特 2016年3月14日 - 13:03

啊, 可能我没表达清楚, 我的意思是:
static_cast "尖括号" void (Newspaper:: *)(const QString &, const QDate &date = QDate::currentDate()) "尖括号" 这样的写法本身编译器就会报错~
只能写作不带默认参数的版本:
static_cast "尖括号" void (Newspaper:: *)(const QString &, const QDate &date) "尖括号"

豆子 2016年3月14日 - 13:11

的确是的,因为这是一种错误写法,只是提示说不能这么写

王新宇 2016年3月30日 - 13:27

至于 static_cast 和 dynamic_cast,二者应用于不同的场景,前者提供类似 C 的强制转换,后者是子类向父类的类型转换----是父类向子类转换吧,子类向父类的类型转换一定会成功呀!

回复
Kian 2016年4月26日 - 10:34

由于项目需要用singleShot调用一个带参数的函数,就使用了新的语法,但是编译器报错:
DEVICE_INDEX deviceIndex; //DEVICE_INDEX为结构体
QTimer::singleShot(100, [=](DEVICE_INDEX index){mapWidget->test_function(index);}(deviceIndex)); //编译器报错error: invalid use of void expression

如果是下面这种写法不会报错:
DEVICE_INDEX deviceIndex;
QTimer::singleShot(100, [=](){mapWidget->test_function(deviceIndex);});
请问第一种写法为什么会报错呢?Qt版本5.5.1.

回复
豆子 2016年4月26日 - 15:07

你可能是和 JS 之类语言的 lambda 表达式混淆了。这么写:

    function (index) { ... } (deviceIndex);

表示定义一个匿名函数,然后直接调用这个函数。但是在信号槽连接中,lambda 表达式是由 Qt 调用的,不是你自己调用。如果你要在 lambda 表达式中使用外部定义的 deviceIndex,只需写

    [=]() { ... deviceIndex ... }

即可。C++ 的 lambda 捕获是通过 [=] 完成的。

回复
helloworld 2017年1月10日 - 18:50

Send sender("new paper");
Recive reveiver;
// QObject::connect(&sender,SIGNAL(Send::newpaper(const QString &)),&reveiver,SLOT(Recive::received(const QString &)));//这种不行,不知道原因,控制台会打印没有信号发出
//下面这种就可以 QObject::connect(&sender,&Send::newpaper,&reveiver,&Recive::received);

回复
豆子 2017年1月12日 - 09:57

使用旧的 SIGNAL/SLOT 语法只需要写函数名,前面不要加上类名,所以你的写法是错误的,应该是 QObject::connect(&sender,SIGNAL(newpaper(const QString &)),&reveiver,SLOT(received(const QString &)));

回复
Am 2019年7月15日 - 17:32

int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);

Newspaper newspaper("Newspaper A");
Reader reader;
QObject::connect(&newspaper, &Newspaper::newPaper,
&reader, &Reader::receiveNewspaper);
newspaper.send();

return app.exec();
}
豆子大大,上面的主函数创建了QCoreApplication并且在return的时候开启事件循环,这里是不是程序没有窗口也就没法关闭窗口结束事件循环,事件循环一直继续,除非在编译器上结束程序

回复
豆子 2019年8月16日 - 21:36

是的,相当于是一个无限循环

回复
Owen 2019年10月22日 - 16:15

豆子老师您好,我在测试带默认参数的槽函数的时候使用了零参数的信号函数(槽函数和信号函数都各只有一个,不存在重载的情况),按理来说槽函数带上了默认参数所以可以运行,但是ide还是提示我说槽函数参数比信号函数的参数多,然后我上Qt wiki查了一下发现下面的信息:
Default arguments in slot

If you have code like this:

class A : public QObject {
Q_OBJECT
public slots:
void someSlot(int foo = 0);
};

The old method allows you to connect that slot to a signal that does not have arguments. But I cannot know with template code if a function has default arguments or not. So this feature is disabled.

There was an implementation that falls back to the old method if there are more arguments in the slot than in the signal. This however is quite inconsistent, since the old method does not perform type-checking or type conversion. It was removed from the patch that has been merged.

其中倒数第二段话是不是说明了信号槽的这种特性已经被摒弃了?
相关网页:https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot

回复
豆子 2019年11月2日 - 16:36

如果存在默认参数,使用新的语法写法比较复杂;但是旧语法又不能有编译期类型检查。所以个人感觉官方是推荐不要使用默认参数。

回复
tron 2020年10月9日 - 11:05

>如果槽函数的参数数目比信号的多,,在槽函数中就使用到这些参数的时候,实际这些参数并不存在(因为信号的参数比槽的少,因此并没有传过来),函数就会报错。这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。

为什么这段的逻辑读着这么怪..."如果槽函数的参数数目比信号的多","这种情况往往有两个原因:一是槽的参数就是比信号的少"...

回复
veni 2021年10月24日 - 11:02

Qt 允许信号和槽的参数数目不一致:槽函数的参数数目可以比信号的参数少。这是因为,我们信号的参数实际是作为一种返回值。正如普通的函数调用一样,我们可以选择忽略函数返回值,但是不能使用一个并不存在的返回值。如果槽函数的参数数目比信号的多,在槽函数中就使用到这些参数的时候,实际这些参数并不存在(因为信号的参数比槽的少,因此并没有传过来),函数就会报错。这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。比如

这一段的
“这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。比如"
移到"但是不能使用一个并不存在的返回值"后面更好吧。
看了半天才看明白= =

回复

回复 豆子 取消回复

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2