Meta-Object System 提供了Qt的信号和槽机制以及对象之间的互相通信,运行时的信息和动态属性系统。
三个必要条件:
1. QObject的子类
2. 宏Q_OBJECT
3. Meta Object Compiler
Qt官方建议对自定义的QObject子类都要加这个宏,但要注意:某些类不是继承自QObject,这些类里加Q_OBJECT就会出错,例如QEvent,QGraphicsItem,QRunnable.
MOC的实现是一个预处理器,使用MOC的方式,所有平台上的标准的C++编译器都能支持Qt。从而不需要实现一个新的跨平台的Qt编译器。moc是为了解决反射的问题,但是一些动态的编程语言(如Python,Ruby等)中,语言本身自带反射功能。Qt程序之所以编译速度慢,主要是因为在 Qt 将源代码交给标准C++编译器,如gcc之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc。
如果工程是用qmake生成Makefile进行编译,那么其中就包含了调用moc的规则,我们不必直接使用moc.exe。moc.exe会读取头文件,查看是否有Q_OBJECT宏定义,如果有则根据这个头文件生成相应的moc_.cpp,该文件同样将进入编译系统,最终被链接到二进制代码中去,QtCreator生成的代码就是通过编译链接时,把moc_widget.o与其他目标文件链接到一起,这种方式不用改源代码,相对而言比较顺眼。
moc_.cpp主要实现了头文件中的Q_OBJECT宏和SIGNAL,也就是将Qt扩展的语法去掉,再交给C++编译器。
SIGNAL只在头文件做声明,但我们写Qt程序时从不进行实现,因为它是在moc_.cpp里实现的,
例如无参数信号void Text();
的实现:
void MainWindow::Text()
{
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
有参数信号void time(QDateTime t);
的实现:
void MainWindow::time(QDateTime _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
主要调用QMetaObject::activate方法,staticMetaObject是这个类默认的一个静态元对象。第三个参数是信号对应的序数。第四个参数如果是nullptr则代表信号无参数,否则代表参数的指针。
moc文件也可以在cpp文件中被include
,Qt官方示例很多就是这样做的,我们也可以这样写常见的MainWindow程序:
#include
#include
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent=0):
QMainWindow(parent)
{}
~MainWindow()
{}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow m;
m.resize(800,600);
m.show();
return a.exec();
}
编译之前先运行qmake,以处理宏Q_OBJECT,生成main.moc。如果没有#include "main.moc"
,会报错:
无法解析3个外部符号,即moc文件中实现的3个函数:qt_metacall, qt_metacast, metaObject
元对象系统涉及到的类: QMetaType, QMetaMethod, QMetaObject, QMataClassInfo, QMetaEnum, QMetaDataReaderControl, QMetaProperty,
QMataType是Qt所支持的元类型,有个Type的枚举,这个枚举列表中的所有类型是Qt信号槽机制本身就支持的,大部分的类型实际在宏QT_FOR_EACH_STATIC_TYPE
中包含。如果是自定义的类型需要用qRegisterMetaType
去注册元类型才能被Qt的信号槽所识别。
QMetaMethod有个重要的成员函数invoke
,支持跨线程调用,是基于元对象系统的。
QMetaObject是元对象类本身,实现了QMetaObject::invokeMethod
, 功能和QMetaMethod没有什么区别。提供了元对象系统的基本方法,包括方法/信号槽/属性的数目/序数等等方法。
QMataClassInfo提供了类的附加信息。
元对象系统的一些常用工具: