The QMetaObject class contains meta-information about Qt objects.
The Qt Meta-Object System in Qt is responsible for the signals and slots inter-object communication mechanism, runtime type information, and the Qt property system. A single QMetaObject instance is created for each QObject subclass that is used in an application, and this instance stores all the meta-information for the QObject subclass. This object is available as QObject::metaObject().
Qt信号槽原理 - sherlock_lin - 博客园
https://ift.tt/kBDPnrE
sherlock_lin
使用Qt已经好几年了,一直以为自己懂Qt,熟悉Qt,使用起来很是熟练,无论什么项目,都喜欢用Qt编写。但真正去看Qt的源码,去理解Qt的思想也就近两年的事。
本次就着重介绍一下Qt的核心功能--信号槽机制,相信接触过Qt的人都能很熟悉地使用,甚至,大部分人还能轻松地说出信息槽的几种用法。但是信号槽的核心可不是简单说说就能说清楚的。
那么,本次,就从Qt的源码中讲解一下信号槽的机制。
其实,直到写这篇文章,我也没有完全看明白相关的源码,只是明白了其中的大部分以及使用机制,其中还有很多细节的,留待以后整理。
如果错误还请大家指正。
Qt版本:Qt 5.5.1
系统:windows 10
在阅读本文前,希望你能:
以下将按照SIGNAL/SLOT宏定义连接信号槽的方式做讲解
接下来将会从按照以下的步骤来进行分析:
3.1、Qt的元数据系统
没看过Qt源码的同学可能会对QMetaObject有些陌生,我们打开Qt手册,查看此类的说明,介绍如下:
这里是说,QMetaObject包含了Qt的元对象信息。元对象机制类似Java的反射机制。通过继承QObject,并在定义类是添加一定Qt内置宏,能在运行时动态获取Qt的信号槽、类型信息以及相关属性。
一个简答的例子
如上,一个简单的例子,通过QMetaObject,我们得到了该对象的类名、父类名、方法并调用了该方法
怎么样,熟悉Java的小伙伴已经发现了,这不就是Java的反射吗,谁说C++没有反射呢
那么,Qt是如何实现”反射“的呢?答案是使用moc预编译
3.2、moc编译
moc全称Meta-Object Compiler,即元对象编译器。我们可以在Qt的安装目录的bin文件下看到moc工具,moc.exe。Qt的构建的时候,会调用该工具生成moc文件,我们在编译目录下看到的moc_xxx.cpp文件就是该工具生成的。
Qt的MinGW版本使用的是qmake进行项目管理,它和cmake功能类似,但没有后者强大。使用qmake生成Makefile后,我们打开Makefile文件,我们可以狠清楚地看到有一个调用moc.exe工具的地方,代码太多,就不列出来了。
此外,我们还发现,并不是所有的代码都会生成moc_xxx.cpp文件的,只有使用了 Q_OBJECT 宏的类文件,才会生成。没有错,moc工具就是根据 Q_OBJECT 宏来生成moc_xxx.cpp文件的,而实现“反射”的元数据系统的也是依靠Q_OBJECT的。
到此,我们其实已经能够大概理清qmake项目的构建步骤了。步骤和常用的cmake项目类似,区别就是,qmake生成的Makefile文件种,会写有调用moc工具的指令,以达到moc_xxx.cpp文件的生成。
我们可以使用moc工具手动生成moc_xxx.cpp,使用指令 moc.exe mainwindow.h,即会在控制台打印moc文件信息,也可以使用 -o 参数来将生成的内容写入文件,其余参数可以使用 moc.exe -h 来查看
3.3、Q_OBJECT
我们可以从源代码中查看 Q_OBJECT 的内容,这里调整一个格式,使用 Q_OBJECT 宏之后,会在类定义的开头多出以下代码:
可以看到,这里多出几个方法和一些变量
这里的几个方法都没有实现体,因为实现部分会有 moc 工具生成,在moc_xxx.cpp 文件中可以查看这些方法的实现体
3.4、signals和slots
signals 用于声明自定义信号,slots 用于声明槽函数,emit 用于发送信号,我们可以从源码中查看这三个宏定义
可以看出,这三个宏几乎什么都没有做,signals 就是声明所谓的信号是public方法,而slots和emit更是为空,标准C++在编译的时候,根本不受这三个宏的影响,那么它们的用处在哪里呢?在moc工具调用和connect连接的时候。
打开moc_xxx.cpp文件,对比查看信号
其实,信号就是方法,而 emit clickButton() 发送信号,就是调用 clickButton() 方法,换言之,触发信号,就算不要emit也无妨
3.5、SIGNAL()和SLOT()
查看源码
qFlagLocation() 源码如下:
store() 方法
所以 SIGNAL(clickButton()) 宏展开为 qFlagLocation("2"clickButton(int) QLOCATION)
SLOT() 同理,这里的1和2,最后会添加到信号槽的前面,其实是为了区分信号和槽,源码中还有一个0在 METHOD() 宏
qFlagLocation 方法的作用是将信号槽转换成字符串保存起来,store 方法中,locations是个二维数组,而 idx 每次都加一,保证信号和槽的不同的方法存储在不同的数组中。
我们也可以在代码中打印出来看下:
3.6、connect方法
最后,就是最关键的connect方法,做了一些简单的注释
方法代码很多很杂,但无非就是检查信号槽的格式,获取参数列表, 最后保存起来
3.7、触发信号
这时候再回过头来看3.4中的信号触发,我们知道,emit信号就是调用moc文件中的方法,方法的核心就是 QMetaObject::activate()
直接看该方法中调用槽函数的一段
由上面代码,我们大概可以理解信号槽的几种连接方式:
本次的源码因为种种原因,看的不是很详细,但是理解Qt的信号槽机制绰绰有余了
直到这里,信号槽的逻辑已经显而易见了,它就是一个变种的观察者模式,槽的信息保存在信号对象中也就是设置回调函数,触发信号也就是执行回调函数,只是Qt库将其中的各种操作细节封装起来了,所以,使用起来,不去关注设计模式的细节,也就容易很多了。不得不说,无论是从设计思路,还是开发技巧上看,Qt的开发者真的很牛叉。
信号槽机制是Qt首创,但不是其独有,其他各类C++流行框架也都是互相借鉴,C++标准库的预备役的boost中也有信号槽机制的实现。如果平时开发中需要用到信号槽机制,但是又不想引入这些庞大的类库,可以使用轻量级别的信号槽库:http://sigslot.sourceforge.net,该库不详细介绍,有兴趣的小伙伴自己学习把。
via www.cnblogs.com https://www.cnblogs.com
October 22, 2024 at 07:19PM