Skip to content

Latest commit

 

History

History
108 lines (74 loc) · 7.21 KB

信号槽机制.md

File metadata and controls

108 lines (74 loc) · 7.21 KB

信号槽机制

基于观察者模式、Moc反射机制和元对象系统的全局事件通信机制

Qt中的信号和槽是一种事件驱动的机制,用于在对象之间传递消息和实现对象间的通信。信号和槽的本质可以解释为以下两个方面:

信号和槽本质是函数:

在Qt中,信号和槽本质上是函数,信号是一种特殊的函数,它不包含函数体,只有函数声明和参数列表,用于向外部发出某种事件的通知。槽也是一种函数,它是一个普通的成员函数,用于处理信号发出的事件。在信号和槽连接时,实际上是将信号函数和槽函数通过一个中介对象(连接器)连接起来,使得信号函数能够调用槽函数,从而实现对象间的通信。

信号和槽本质是元对象系统的一部分:

在Qt中,信号和槽是元对象系统的一部分,元对象系统是一个用于支持Qt元编程的框架,它允许在运行时动态地查询和操作对象的元数据,包括类名、属性、方法等。信号和槽的实现依赖元对象系统的元数据,每个QObject对象在创建时都会生成一个元对象,其中包含了该对象的所有信息,包括信号和槽的声明和实现。当信号和槽连接时,会根据元数据进行检查和匹配,保证信号和槽的正确连接和调用。

综上所述,信号和槽本质上是函数,它们通过元对象系统实现对象间的通信,使得对象之间能够相互响应和交互,是Qt中非常重要的一种机制。

优点与缺点

基于观察者模式的全局事件通信机制在很多前端开发框架中都有用到,优点是不受界面约束,可以在任意时间任意界面进行两两通信,有效减少参数透传和回调。

优点:

  • ①类型安全。需要关联的信号槽的签名必须是等同的。即信号的参数类型和参数个数同接受该信号的槽的参数类型和参数个数相同。若信号和槽签名不一致,编译器会报错。
  • ②松散耦合。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无需知道是那个对象的那个信号槽接收它发出的信号,它只需在适当的时间发送适当的信号即可,而不需要关心是否被接受和那个对象接受了。Qt就保证了适当的槽得到了调用,即使关联的对象在运行时被删除。程序也不会奔溃。
  • ③灵活性。一个信号可以关联多个槽,或多个信号关联同一个槽。

不足: 速度较慢。与回调函数相比,信号和槽机制运行速度比直接调用非虚函数慢10倍。 原因:

  • ①需要定位接收信号的对象。
  • ②安全地遍历所有关联槽。
  • ③编组、解组传递参数。
  • ④多线程的时候,信号需要排队等待。(然而,与创建对象的new操作及删除对象的delete操作相比,信号和槽的运行代价只是他们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的。)

信号槽使用流程

  • MOC查找头文件中的signal与slots,标记出信号槽。将信号槽信息储存到类静态变量staticMetaObject中,并按照声明的顺序进行存放,建立索引。
  • connect链接,将信号槽的索引信息放到一个双向链表中,彼此配对。
  • emit被调用,调用信号函数,且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数。
  • active函数在双向链表中找到所有与信号对应的槽索引,根据槽索引找到槽函数,执行槽函数。

信号槽的多种写法

connect(信号发出者,信号,信号接收者,槽,连接方式(隐藏默认自动连接))//五个参数

四种写法:

  • 用宏connect(this,SIGNAL(clicked()),this,SLOT(colse()));
  • 用函数指针: connect(this,&mainwindow::my_signal,this,&mainwindow::my_slot);
  • 用重载函数指针
Qoverload connect(this,Qoverload<参数>::of(&mainwindow::my_signal),this,Qoverload<参数>::of(&mainwindow::my_slot));
  • lambda表达式(匿名函数)
 connect(this,&mainwindow::my_signal,this,[=]{qDebug()<<100;});
 连接方式:自动连接(默认连接方式)
 直接连接(用于单线程,自动匹配)
 队列(用于多线程也可用于单线程,自动匹配)
 阻塞队列(跨线程,多线程)
 唯一连接(跨线程,多线程)

信号槽调用线程(第五个参数设置)

  1. Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
  2. Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
  3. Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
  4. Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
  5. Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。

Qt4和Qt5中信号槽差异

  • 编译期:检查信号与槽是否存在,参数类型检查,Q_OBJECT是否存在
  • 信号可以和普通的函数、类的普通成员函数、lambda函数连接(而不再局限于信号函数和槽函数)
  • 参数可以是 typedef 的或使用不同的namespace specifier
  • 可以允许一些自动的类型转换(即信号和槽参数类型不必完全匹配)

槽函数处理多个信号函数

  • 在发送信号时,携带标识符参数,比如int数值,这样在槽函数触发是可以知道是哪个信号发出的。
  • 在槽函数内有获取发送信号的函数,通过sender()函数获取发送信号;
    QObject* obj = sender();
	QPushButton* pBtn = dynamic_cast<QPushButton*>(obj);

Qt如果一个信号的处理方法一直未被执行有哪些可能性

  1. 断开了
  2. 连接的时候失败了
  3. 多线程的时候在排队或者启动锁死了。

信号重载了,如何确定连接哪个信号?

采用函数指针确定连接哪个信号。

槽函数参数、信号的参数

槽函数的参数可以少于信号的参数。

  • 槽函数本身参数比信号的少
  • 槽函数参数带有默认参数

槽函数的参数是否可以比信号的参数多? 也可以。唯一的情况就是槽函数参数带有默认参数,除去默认参数外,槽函数的参数必须小于等于信号的参数。