1、说明
使用Qt已经好几年了,一直以为自己懂Qt,熟悉Qt,使用起来很是熟练,无论什么项目,都喜欢用Qt编写。但真正去看Qt的源码,去理解Qt的思想也就近两年的事。
本次就着重介绍一下Qt的核心功能–信号槽机制,相信接触过Qt的人都能很熟悉地使用,甚至,大部分人还能轻松地说出信息槽的几种用法。但是信号槽的核心可不是简单说说就能说清楚的。
那么,本次,就从Qt的源码中讲解一下信号槽的机制。
其实,直到写这篇文章,我也没有完全看明白相关的源码,只是明白了其中的大部分以及使用机制,其中还有很多细节的,留待以后整理。
如果错误还请大家指正。
2、环境以及知识点
Qt版本:Qt 5.5.1
系统:windows 10
在阅读本文前,希望你能:
- 熟练使用C++,了解make的编译方法和过程;
- 熟练使用Qt的信号槽功能,对信号槽的写法以及4和5的区别了如指掌;
- QMetaObject元数据系统;
- 懂一些设计模式,能理解观察者模式;
3、信号槽源码分析
以下将按照SIGNAL/SLOT宏定义连接信号槽的方式做讲解
接下来将会从按照以下的步骤来进行分析:
- Qt元数据系统;
- moc预编译;
- Q_OBJECT宏;
- signals和slots关键字以及emit;
- SIGNAL()和SLOT()宏;
- connect 方法;
- 触发信号;
3.1、Qt的元数据系统
没看过Qt源码的同学可能会对QMetaObject有些陌生,我们打开Qt手册,查看此类的说明,介绍如下:
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().
这里是说,QMetaObject包含了Qt的元对象信息。元对象机制类似Java的反射机制。通过继承QObject,并在定义类是添加一定Qt内置宏,能在运行时动态获取Qt的信号槽、类型信息以及相关属性。
一个简答的例子
void MainWindow::onClickButton()
{
qDebug()<<"on click button";
const QMetaObject* metaObject = this->metaObject();
qDebug()<<metaObject->className();
qDebug()<<metaObject->superClass()->className();
int methodIndex = metaObject->indexOfMethod("testFunction()");
qDebug()<<methodIndex;
qDebug()<<metaObject->method(methodIndex).name();
metaObject->method(methodIndex).invoke(this);
QMetaObject::invokeMethod(this, "testFunction");
}
如上,一个简单的例子,通过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 宏之后,会在类定义的开头多出以下代码:
public:
Q_OBJECT_CHECK
QT_WARNING_PUSH
Q_OBJECT_NO_OVERRIDE_WARNING
static const QMetaObject staticMetaObject;
virtual const QMetaObject *metaObject() const;
virtual void *qt_metacast(const char *);
virtual int qt_metacall(QMetaObject::Call, int, void **);
QT_WARNING_POP
QT_TR_FUNCTIONS
private:
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
struct QPrivateSignal {};
可以看到,这里多出几个方法和一些变量
- 属性staticMetaObject,元数据对象,可以从中获取当前类的元数据&