深入理解Qt信号和槽机制、信号和槽中的Lambda表达式

转载地址:https://blog.csdn.net/qq_40194498/article/details/79647356

对于事件处理,MFC中使用的是消息映射机制,Qt使用的是信号和槽机制,在我看来,Qt的信号和槽比MFC功能更强大,也更灵活。
1、信号和槽的简单介绍:
一般格式:

connect(Sender,SIGNAL(signal),Receiver,SLOT(slot));  
connect(信号发送者,信号,信号接受者,槽函数);
  • 1
  • 2

做个很简单的比喻:运动比赛,裁判员鸣枪,运动员起跑,信号发送者是裁判,信号是枪声;信号接受者是运动员,槽函数(对信号做出的相应)是起跑。

所有的QObject都可以使用信号和槽机制,而Qt中的大部分类都是继承于QObject,SIGNAL()和()SLOT()是Qt定义的两个宏,他们返回其参数的C语言风格的字符串(const *char,信号加前缀2,槽加前缀1),因此下面的两个语句是相同的:
connect(Object2,SIGNAL(clicked()),Object3,SLOT(functionA()));
connect(Object2,”2clicked()”,Object3,”1functionA()”);
例如:

    QTimer *mytimer = new QTimer(this);
    mytimer->start(1000);
    connect(mytimer,"2timeout()",this,"1my_slot()");
  • 1
  • 2
  • 3

需要注意的是:
不论是信号还是槽函数,在SIGNAL()和SLOT()中使用时,参数只能包含变量类型,不能包含变量名

2、信号和槽的变化:

//一个信号可以与另一个信号相连 
connect(Object1,SIGNAL(signal1),Object2,SIGNAL(signal2)); 
//同一个信号可以与多个槽相连,此时调用槽的顺序是随机的
connect(Object1,SIGNAL(signal1),Object2,SLOT(slot1)); 
connect(Object1,SIGNAL(signal1),Object3,SLOT(slot2));
//同一个槽也可以响应多个信号 
connect(Object1,SIGNAL(signal1),Object3,SLOT(slot1)); 
connect(Object2,SIGNAL(signal2),Object3,SLOT(slot1)); 
//连接也可以被移除,当然,这种情况很少见,因为对象被删除时,Qt会自动移除该对象相关的所有连接   
 disconnect(Sender,SIGNAL(signal),Receiver,SLOT(slot));  
 //要把信号和槽(或信号)成功连接,被连接的两者,其参数必须有相同的顺序和类型,这里有个例外是,如果信号的参数比它连接的槽的参数多,多余的参数会被简单的忽略掉
connect(Object1,SIGNAL(signal(int,const QString&)),Object2,SIGNAL(signal(int,const QString&)); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3、Qt5中信号和槽的扩展
3.1、
C++11中增加了兰布达表达式(Lambda),在Qt5的信号和槽中,也可以使用兰布达表达式:
由于用了C++11的特性,需要在.pro文件中添加:CONFIG += C++11

    QTimer *mytimer = new QTimer(this);
    mytimer->start(1000); 
//Lambda表达式对应的信号必须是函数指针形式
connect (mytimer ,&QTimer::timeout,
              [=]()
     { //dosomething,信号发出之后,需要做的事情
     }
     );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

方括号内代表可用的实体(变量、控件等),多个可用逗号隔开,方括号内还可以放一些好用的运算符:

[=] () {}
Lambda函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量),此时Lambda表达式中,实体默认为只读的,在大括号内不能进行修改,如果想修改,可以用mutable修饰,如 [=]() mutable

[&] () {}
Lambda函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)

[this] () {} 函数体内可以使用Lambda所在类中的所有成员变量
注意:尽量使用 = 而不使用 & ,以免造成内存问题

扫描二维码关注公众号,回复: 3651610 查看本文章

3.2、

QPushButton *b = new QPushButton(this);
connect(&b,&QPushButton::pressed,this,&MainWidget::close); 
//Qt5引入了信号槽的新语法:使用函数指针能够获得编译期的类型检查。 

//Qt4版本信号槽
connect(b,SIGNAL(pressed()),this,SLOT(close())); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3.3、
lambda表达式的返回值问题可移步我的另一篇博客:
https://blog.csdn.net/qq_40194498/article/details/79861526
4、信号和槽的注意事项

  • 信号和槽的效率是非常高的,不过同真正的回调函数比较起来,由于增加了灵活性,因此在速度上还是有所损失,追求高效率的实时系统中,应尽量少用;
  • 信号和槽机制和普通函数的调用相同,如果使用不当的话,在程序执行时也有可能产生死循环。
  • 函数指针不能作为信号或槽的参数
  • 信号和槽不能有缺省参数
  • 当信号的发送者为定时器时,尽量不要把connect函数放在if、while等可能会导致信号槽阻塞的代码段里面。我做过这样一个事情:启动程序时启动定时器,用一个If语句判断输入是否合法,判断通过则调用发送者为定时器的connect函数,结果本该一分钟触发一次的槽函数在一秒内被连续触发了十几次;
  • 当子线程中需要使用信号槽机制时,必须在子线程头文件中加宏Q_OBJECT,当我们创建继承于QThread的子线程时,这个宏并不会自动添加。手动添加之后,构建项目可能会报错:undefined reference to ‘vtable for’,此时我们需要将子线程类从项目中移除(不要从磁盘上删除),然后重新添加,QtCreator就会重新解析此类,再编译就不再会出现上述错误.
  • 待续

--------------------- 作者:52_赫兹的鲸 来源:CSDN 原文:https://blog.csdn.net/qq_40194498/article/details/79647356?utm_source=copy 版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/sunxiaopengsun/article/details/82995086