系列文章目录
文章目录
前言
Qt是一款跨平台的C++ GUI开发框架,具有开发效率高、易于维护等优点。Qt核心机制是Qt框架能够发挥其最大作用的关键,它涉及到Qt运行时系统、信号与槽机制、事件机制、对象模型等方面。在Qt应用程序的开发过程中,对Qt核心机制的理解是非常重要的。本博客将深入探讨Qt核心机制的相关知识点,为读者提供全面而深入的了解和实践指导。
一、元对象系统
元对象系统分为三大类:QObject类、Q_OBJECT宏和元对象编译器 moc 。Qt的类包含Q_OBJECT宏moc编译器会对该类编译成标准的C++代码。
二、Qt运行时系统
Qt框架提供了一个基于事件循环的运行时系统,通过该系统可以处理多个任务同时执行的问题;Qt运行时系统:Qt的运行时系统是一个轻量级的应用程序框架,它提供了线程、定时器、事件处理、日志记录等基本功能。
三、对象模型
Qt采用了基于对象的编程思想,所有的GUI组件都被看做一个对象。每个对象都有一个唯一的对象名,可以通过对象名来访问它们。Qt的对象模型支持类的继承、重载以及多重继承等特性。
四、信号与槽机制
Qt采用了一种高效的通信机制——信号与槽。当一个对象发出一个信号时,与之相连的槽函数就会被自动调用,从而实现了对象之间的通信,使得应用程序可以以一种松散耦合的方式进行构建。。
1.信号与槽的概述
信号和槽机制是QT的核心机制,要精通QT编程就必须对信号和槽有所了解。信号和槽是一种高级接口,应用于对象之间的通信,它是QT的核心特性,也是QT区别于其它工具包的重要地方。信号和槽是QT自行定义的一种通信机制,它独立于标准的C/C++语言,因此要正确的处理信号和槽,必须借助一个称为 moc (Meta Object Compiler)的QT工具,该工具是一个C++预处理程序,它为高层次的事件处理自动生成所需要的附加代码。
在所有从 QObject或其子类(例如Qwidget)派生的类都能够包含信号和槽。当对象改变其状态时,信号就由该对象发射(emit)出去,这就是对象所要做的全部事情,它不知道另一端是谁在接收这个信号。这就是真正的信息封装,它确保对象被当作一个真正的软件组件来使用。槽用于接收信号,但它们是普通的对象成员函数。一个槽并不知道是否有任何信号与自己相连接。而且,对象并不了解具体的通信机制。
2.Qt信号和槽的本质
- 回调函数。信号或是传递值,或是传递动作变化;槽函数响应信号或是接收值,或者根据动作变化来做出对应操作。
3.信号与槽实现的具体流程
(1)moc查找头文件中的signals,slots,标记出信号和槽。
(2)将信号槽信息存储到类静态变量staticMetaObject中,并且按声明顺序进行存放,建立索引。
(3)当发现有connect连接时,将信号槽的索引信息放到一个map中,彼此配对。
(4)当调用emit时,调用信号函数,并且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数
(5)通过active函数找到在map中找到所有与信号对应的槽索引
(6)根据槽索引找到槽函数,执行槽函数。
4.Qt信号槽机制与优势与缺点
优点:
①类型安全。需要关联的信号槽的签名必须是等同的。即信号的参数类型和参数个数同接受该信号的槽的参数类型和参数个数相同。若信号和槽签名不一致,编译器会报错。
②松散耦合。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无需知道是那个对象的那个信号槽接收它发出的信号,它只需在适当的时间发送适当的信号即可,而不需要关心是否被接受和那个对象接受了。Qt就保证了适当的槽得到了调用,即使关联的对象在运行时被删除。程序也不会奔溃。
③灵活性。一个信号可以关联多个槽,或多个信号关联同一个槽。
缺点:
速度较慢。与回调函数相比,信号和槽机制运行速度比直接调用非虚函数慢10倍。
5.Qt信号与槽的五种连接方式
(1)Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
(2)Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数和信号发送者在同一线程。效果看上去就像是直接在信号发送位置调用了槽函数,效果上看起来像函数调用,同步执行。 emit语句后面的代码将在与信号关联的所有槽函数执行完毕后才被执行。
(3)Qt::QueuedConnection :信号发出后,信号会暂时被放到一个消息队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作。 emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。
(4)Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。而且接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
(5)Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是为了避免重复连接。
6.示例
默认连接
Test1::Test1(QWidget *parent) : QWidget(parent)
{
QElapsedTimer timer;
timer.start();
ObjTest *obj = new ObjTest();
connect(this,SIGNAL(signal1()),obj,SLOT(doTest()));
qDebug() << "emit in thread:" << QThread::currentThreadId();
emit signal1();
qDebug() << timer.elapsed() << "ms";
}
void ObjTest::doTest()
{
qDebug() << "doTest in thread:" << QThread::currentThreadId();
QThread::currentThread()->msleep(1000);
}
运行结果:
队列连接
Test1::Test1(QWidget *parent) : QWidget(parent)
{
QElapsedTimer timer;
timer.start();
ObjTest *obj = new ObjTest();
connect(this,SIGNAL(signal1()),obj,SLOT(doTest()),Qt::QueuedConnection);
qDebug() << "emit in thread:" << QThread::currentThreadId();
emit signal1();
qDebug() << timer.elapsed() << "ms";
}
void ObjTest::doTest()
{
qDebug() << "doTest in thread:" << QThread::currentThreadId();
QThread::currentThread()->msleep(1000);
}
运行结果:
五、事件机制
Qt的事件机制是基于运行时系统的事件循环实现的。当一个事件发生时,它会被加入到事件队列中,等待被处理。Qt提供了一套事件处理机制,可以让开发者灵活地处理各种事件。
六、内存管理机制
Qt采用了一套内存管理机制,可以自动管理对象的生命周期,从而避免了内存泄漏的问题。同时,Qt还提供了一个父子关系机制,可以方便地管理对象之间的依赖关系。