Qt--Meta-Object Model
来源:互联网 发布:邪恶力量第十二季 知乎 编辑:程序博客网 时间:2024/05/21 14:59
Meta_Object Model
Qt meta-object系统基于三个方面:
1、QObject提供一个基类,方便派生类使用meta-object系统的功能;
2、Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性、信号、槽;
3、Meta Object编译器(MOC),为每个QObject派生类生成代码,以支持meta-object功能。
QObject定义了从一个QObject对象访问meta-object功能的接口,Q_OBJECT宏用来告诉编译器该类需要激活meta- object功能,编译器在扫描一个源文件时,如果发现类的声明中有这个宏,就会生成一些代码来为支持meta-object功能——主要是生成该类对应 MetaObject类以及对QObject的函数override。
QObject和QMetaObject:
顾名思义,QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特 有的signal&slot信息。
QObject::metaObject ()方法返回一个QObject对象对应的metaobject对象,注意这个方法是virtual方法。如上文所说,如果一个类的声明中包含了 Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个 QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的 metaobject。
如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的 signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元 数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义 signal&slot和Property。
这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。
QMetaObject提供的信息:
下面通过QMetaObject的接口来解读QMetaObject提供的信息:
1、基本信息
const char * className () const; const QMetaObject * superClass () const
2、classinfo: 提供额外的类信息。其实就是一些名值对。 用户可以在类的声明中以Q_CLASSINFO(name, value)方式添加。
int classInfoCount () const int classInfoOffset () const QMetaClassInfo classInfo ( int index ) const int indexOfClassInfo ( const char * name ) const
3、contructor:提供该类的构造方法信息
QMetaMethod constructor ( int index ) const int constructorCount () const int indexOfConstructor ( const char * constructor ) const
4、enum:描述该类声明体中所包含的枚举类型信息
QMetaEnum enumerator ( int index ) constint enumeratorCount () constint enumeratorOffset () constint indexOfEnumerator ( const char * name ) const
5、method:描述类中所包含方法信息:包括property,signal,slot等,包括祖先类,如何组织暂时不确定。
QMetaMethod method ( int index ) constint methodCount () constint methodOffset () constint indexOfMethod ( const char * method ) constint indexOfSignal ( const char * signal ) constint indexOfSlot ( const char * slot ) const
6、property:类型的属性信息
QMetaProperty property ( int index ) const int propertyCount () const int propertyOffset () const int indexOfProperty ( const char * name ) const QMetaProperty userProperty () const //返回类中设置了USER flag的属性,(难道只能有一个这样的属性?)
注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,
Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。
Qt扩展了C++的语法,加入了signals,slots,emit,
moc文件分析
我们知道Qt 不是使用的“标准的” C++ 语言,而是对其进行了一定程度的“扩展”。这里我们从Qt新增加的关键字就可以看出来:signals、slots 或者 emit。所以有人会觉得 Qt 的程序编译速度慢,这主要是因为在 Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc。
moc 全称是 Meta-Object Compiler,也就是“元对象编译器”。Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果它发现在一个头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。这个新的文件名字将会是原文件名前面加上 moc_ 构成。这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此我们可以知道,这个新的文件不是“替换”掉旧的文件,而是与原文件一起参与编译。另外,我们还可以看出一点,moc 的执行是在预处理器之前。因为预处理器执行之后,Q_OBJECT 宏就不存在了。
让我们用一个实例来分析下moc在搞什么鬼,添加一个新类MyClass派生自QObject且使用Q_OBJECT宏,
编译后发现在生成目录下会产生一个moc_myclass.cpp文件,代码贴上来:
myclass.h
#ifndef MYCLASS_H#define MYCLASS_H#include <QObject>class MyClass : public QObject{ Q_OBJECT Q_CLASSINFO("AUTHOR", "HeWei") Q_CLASSINFO("VERSION", "1.0") Q_CLASSINFO("TIME", "2017/01/01") Q_PROPERTY(int prop1)public: enum EMyClass{ EMyClass_1 = 1, EMyClass_2 = 2, }; Q_ENUMS(EMyClass)public: MyClass(QObject *parent) : QObject(parent) { m_1 = 0; } Q_INVOKABLE MyClass(int initparam, QObject *parent2) : QObject(parent2) { m_1 = initparam; } Q_INVOKABLE int method1(int methodparam) {return 0;} int method2(int method2param) {return 0;}signals: void signal1(int signalparam);public slots: int slot1(int slotparam) {return 0;}private: int m_1;};#endif // MYCLASS_H
moc_myclass.cpp
/****************************************************************************** Meta object code from reading C++ file 'myclass.h'**** Created by: The Qt Meta Object Compiler version 67 (Qt 5.7.0)**** WARNING! All changes made in this file will be lost!*****************************************************************************/#include "../../core01/myclass.h"#include <QtCore/qbytearray.h>#include <QtCore/qmetatype.h>#if !defined(Q_MOC_OUTPUT_REVISION)#error "The header file 'myclass.h' doesn't include <QObject>."#elif Q_MOC_OUTPUT_REVISION != 67#error "This file was generated using the moc from 5.7.0. It"#error "cannot be used with the include files from this version of Qt."#error "(The moc has changed too much.)"#endifQT_BEGIN_MOC_NAMESPACEstruct qt_meta_stringdata_MyClass_t { QByteArrayData data[20]; char stringdata0[161];};#define QT_MOC_LITERAL(idx, ofs, len) \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ qptrdiff(offsetof(qt_meta_stringdata_MyClass_t, stringdata0) + ofs \ - idx * sizeof(QByteArrayData)) \ )static const qt_meta_stringdata_MyClass_t qt_meta_stringdata_MyClass = { {QT_MOC_LITERAL(0, 0, 7), // "MyClass"QT_MOC_LITERAL(1, 8, 6), // "AUTHOR"QT_MOC_LITERAL(2, 15, 5), // "HeWei"QT_MOC_LITERAL(3, 21, 7), // "VERSION"QT_MOC_LITERAL(4, 29, 3), // "1.0"QT_MOC_LITERAL(5, 33, 4), // "TIME"QT_MOC_LITERAL(6, 38, 10), // "2017/01/01"QT_MOC_LITERAL(7, 49, 7), // "signal1"QT_MOC_LITERAL(8, 57, 0), // ""QT_MOC_LITERAL(9, 58, 11), // "signalparam"QT_MOC_LITERAL(10, 70, 5), // "slot1"QT_MOC_LITERAL(11, 76, 9), // "slotparam"QT_MOC_LITERAL(12, 86, 7), // "method1"QT_MOC_LITERAL(13, 94, 11), // "methodparam"QT_MOC_LITERAL(14, 106, 9), // "initparam"QT_MOC_LITERAL(15, 116, 7), // "parent2"QT_MOC_LITERAL(16, 124, 5), // "prop1"QT_MOC_LITERAL(17, 130, 8), // "EMyClass"QT_MOC_LITERAL(18, 139, 10), // "EMyClass_1"QT_MOC_LITERAL(19, 150, 10) // "EMyClass_2" }, "MyClass\0AUTHOR\0HeWei\0VERSION\0""1.0\0" "TIME\0""2017/01/01\0signal1\0\0signalparam\0" "slot1\0slotparam\0method1\0methodparam\0" "initparam\0parent2\0prop1\0EMyClass\0" "EMyClass_1\0EMyClass_2"};#undef QT_MOC_LITERALstatic const uint qt_meta_data_MyClass[] = { // content: 7, // revision 0, // classname 3, 14, // classinfo 3, 20, // methods 1, 49, // properties 1, 52, // enums/sets 1, 60, // constructors 0, // flags 1, // signalCount // classinfo: key, value 1, 2, 3, 4, 5, 6, // signals: name, argc, parameters, tag, flags 7, 1, 35, 8, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 10, 1, 38, 8, 0x0a /* Public */, // methods: name, argc, parameters, tag, flags 12, 1, 41, 8, 0x02 /* Public */, // signals: parameters QMetaType::Void, QMetaType::Int, 9, // slots: parameters QMetaType::Int, QMetaType::Int, 11, // methods: parameters QMetaType::Int, QMetaType::Int, 13, // constructors: parameters 0x80000000 | 8, QMetaType::Int, QMetaType::QObjectStar, 14, 15, // properties: name, type, flags 16, QMetaType::Int, 0x00095000, // enums: name, flags, count, data 17, 0x0, 2, 56, // enum data: key, value 18, uint(MyClass::EMyClass_1), 19, uint(MyClass::EMyClass_2), // constructors: name, argc, parameters, tag, flags 0, 2, 44, 8, 0x0e /* Public */, 0 // eod};void MyClass::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a){ if (_c == QMetaObject::CreateInstance) { switch (_id) { case 0: { MyClass *_r = new MyClass((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< QObject*(*)>(_a[2]))); if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break; default: break; } } else if (_c == QMetaObject::InvokeMetaMethod) { MyClass *_t = static_cast<MyClass *>(_o); Q_UNUSED(_t) switch (_id) { case 0: _t->signal1((*reinterpret_cast< int(*)>(_a[1]))); break; case 1: { int _r = _t->slot1((*reinterpret_cast< int(*)>(_a[1]))); if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; } break; case 2: { int _r = _t->method1((*reinterpret_cast< int(*)>(_a[1]))); if (_a[0]) *reinterpret_cast< int*>(_a[0]) = _r; } break; default: ; } } else if (_c == QMetaObject::IndexOfMethod) { int *result = reinterpret_cast<int *>(_a[0]); void **func = reinterpret_cast<void **>(_a[1]); { typedef void (MyClass::*_t)(int ); if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&MyClass::signal1)) { *result = 0; return; } } }#ifndef QT_NO_PROPERTIES else if (_c == QMetaObject::ReadProperty) { } else if (_c == QMetaObject::WriteProperty) { } else if (_c == QMetaObject::ResetProperty) { }#endif // QT_NO_PROPERTIES}const QMetaObject MyClass::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_MyClass.data, qt_meta_data_MyClass, qt_static_metacall, Q_NULLPTR, Q_NULLPTR}};const QMetaObject *MyClass::metaObject() const{ return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;}void *MyClass::qt_metacast(const char *_clname){ if (!_clname) return Q_NULLPTR; if (!strcmp(_clname, qt_meta_stringdata_MyClass.stringdata0)) return static_cast<void*>(const_cast< MyClass*>(this)); return QObject::qt_metacast(_clname);}int MyClass::qt_metacall(QMetaObject::Call _c, int _id, void **_a){ _id = QObject::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { if (_id < 3) qt_static_metacall(this, _c, _id, _a); _id -= 3; } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { if (_id < 3) *reinterpret_cast<int*>(_a[0]) = -1; _id -= 3; }#ifndef QT_NO_PROPERTIES else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) { qt_static_metacall(this, _c, _id, _a); _id -= 1; } else if (_c == QMetaObject::QueryPropertyDesignable) { _id -= 1; } else if (_c == QMetaObject::QueryPropertyScriptable) { _id -= 1; } else if (_c == QMetaObject::QueryPropertyStored) { _id -= 1; } else if (_c == QMetaObject::QueryPropertyEditable) { _id -= 1; } else if (_c == QMetaObject::QueryPropertyUser) { _id -= 1; }#endif // QT_NO_PROPERTIES return _id;}// SIGNAL 0void MyClass::signal1(int _t1){ void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; QMetaObject::activate(this, &staticMetaObject, 0, _a);}QT_END_MOC_NAMESPACE
moc源文件中先声明了一个qt_meta_stringdata_MyClass_t结构体和一个 QT_MOC_LITERAL(idx, ofs, len)宏
struct qt_meta_stringdata_MyClass_t {QByteArrayData data[20];char stringdata0[161];};#define QT_MOC_LITERAL(idx, ofs, len) \Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \qptrdiff(offsetof(qt_meta_stringdata_MyClass_t, stringdata0) + ofs \ - idx * sizeof(QByteArrayData)) \)
接着定义了一个static const静态常量qt_meta_stringdata_MyClass_t结构体
static const qt_meta_stringdata_MyClass_t qt_meta_stringdata_MyClass = { { QT_MOC_LITERAL(0, 0, 7), // "MyClass" QT_MOC_LITERAL(1, 8, 6), // "AUTHOR" QT_MOC_LITERAL(2, 15, 5), // "HeWei" QT_MOC_LITERAL(3, 21, 7), // "VERSION" QT_MOC_LITERAL(4, 29, 3), // "1.0" QT_MOC_LITERAL(5, 33, 4), // "TIME" QT_MOC_LITERAL(6, 38, 10), // "2017/01/01" QT_MOC_LITERAL(7, 49, 7), // "signal1" QT_MOC_LITERAL(8, 57, 0), // "" QT_MOC_LITERAL(9, 58, 11), // "signalparam" QT_MOC_LITERAL(10, 70, 5), // "slot1" QT_MOC_LITERAL(11, 76, 9), // "slotparam" QT_MOC_LITERAL(12, 86, 7), // "method1" QT_MOC_LITERAL(13, 94, 11), // "methodparam" QT_MOC_LITERAL(14, 106, 9), // "initparam" QT_MOC_LITERAL(15, 116, 7), // "parent2" QT_MOC_LITERAL(16, 124, 5), // "prop1" QT_MOC_LITERAL(17, 130, 8), // "EMyClass" QT_MOC_LITERAL(18, 139, 10), // "EMyClass_1" QT_MOC_LITERAL(19, 150, 10) // "EMyClass_2" }, "MyClass\0AUTHOR\0HeWei\0VERSION\0""1.0\0" "TIME\0""2017/01/01\0signal1\0\0signalparam\0" "slot1\0slotparam\0method1\0methodparam\0" "initparam\0parent2\0prop1\0EMyClass\0" "EMyClass_1\0EMyClass_2"};
QT_MOC_LITERAL(idx, off, len)三个参数分别代表索引、偏移、长度
通过偏移off[1]和长度len[1]我们可以得到下一个数据的偏移位置off[2]=off[1]+len[1]+1(\0字符串结束符占一个字节)
qt_meta_stringdata_MyClass.stringdata0是一个字符数组,里面记录了MyClass类中所有的标识符,包括类名classname、类信息classinfo、方法(包括信号、槽、一般函数)名method、方法参数、属性名property、枚举名enum、枚举项名
接着定义了一个qt_meta_data_MyClass,这是一个const static uint[]静态常量整型数组。
static const uint qt_meta_data_MyClass[] = { // content: 7, // revision 0, // classname 3, 14, // classinfo 3, 20, // methods 1, 49, // properties 1, 52, // enums/sets 1, 60, // constructors 0, // flags 1, // signalCount // classinfo: key, value 1, 2, 3, 4, 5, 6, // signals: name, argc, parameters, tag, flags 7, 1, 35, 8, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 10, 1, 38, 8, 0x0a /* Public */, // methods: name, argc, parameters, tag, flags 12, 1, 41, 8, 0x02 /* Public */, // signals: parameters QMetaType::Void, QMetaType::Int, 9, // slots: parameters QMetaType::Int, QMetaType::Int, 11, // methods: parameters QMetaType::Int, QMetaType::Int, 13, // constructors: parameters 0x80000000 | 8, QMetaType::Int, QMetaType::QObjectStar, 14, 15, // properties: name, type, flags 16, QMetaType::Int, 0x00095000, // enums: name, flags, count, data 17, 0x0, 2, 56, // enum data: key, value 18, uint(MyClass::EMyClass_1), 19, uint(MyClass::EMyClass_2), // constructors: name, argc, parameters, tag, flags 0, 2, 44, 8, 0x0e /* Public */, 0 // eod};
根据注释,我们可以知道里面定义了revision(moc修正版本号5.7),索引为0的字符串表示classname
3, 14, // classinfo 3代表有3个classinfo,14代表qt_meta_data_MyClass[14]就是classinfo的详细信息记录起点,即
// classinfo: key, value 1, 2, 3, 4, 5, 6,
methods,properties,enums/sets,constructors记录方法类似
flags
signalCount代表信号的数量
接下来的数据也就记录了各自的详细信息
然后就是实现了Q_OBJECT宏声明的函数staticMetaObject、metaObject、qt_metacast、qt_metacall、qt_static_metacall
#define Q_OBJECT \public: \ static const QMetaObject staticMetaObject; \ virtual const QMetaObject *metaObject() const; \ virtual void *qt_metacast(const char *); \ virtual int qt_metacall(QMetaObject::Call, int, void **); \ QT_TR_FUNCTIONS \private: \ static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
staticMetaObject直接返回的是一个QMetaObject的内部数据
const QMetaObject MyClass::staticMetaObject = {{ &QObject::staticMetaObject, qt_meta_stringdata_MyClass.data, qt_meta_data_MyClass, qt_static_metacall, Q_NULLPTR, Q_NULLPTR}
};
对应的是QMetaObject的结构体数据,即超类QMetaObject 指针,标识符字符串数据,元信息,qt_static_metacall调用指针,相关的QMetaObject 指针(一般为空),额外的数据(一般为空)
struct { // private data const QMetaObject *superdata; const QByteArrayData *stringdata; const uint *data; typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **); StaticMetacallFunction static_metacall; const QMetaObject * const *relatedMetaObjects; void *extradata; //reserved for future use } d;
qt_metacast在转换类时中做了一个类名检查,如果不是相关继承类,会返回NuLL。
qt_metacall提供方法调用(qt_static_metacall),属性查询,读写等操作。
此外moc文件中还添加上了我们声明的信号,这就是为什么信号只用我们声明不用定义的原因。
信号实际上也就是一个函数,最终会调用QMetaObject::activate。
emit sig;实际上就是调用这个信号函数罢了
- Qt--Meta-Object Model
- qt meta-object system
- Qt Meta Object
- Qt Meta-Object System
- QT: Meta-Object 系统
- Qt Meta-Object System
- Qt Object Model
- QT的 Meta-Object系统
- QT的Meta-Object系统
- Qt之Meta-Object系统
- Inside QT Series (八):Meta Object Class
- Qt 如何实现的 Meta Object
- Qt 元对象系统(Meta-Object System)
- Qt 元对象系统(Meta-Object System)
- Java reflect vs. Qt Meta-Object
- Qt 元对象系统(Meta-Object System)
- Qt Meta Object system 学习(一)
- Qt Meta Object system 学习(二)
- submint()和onsubmit() 的区别
- HDU
- ORACLE SQL TUNING ADVISOR 使用方法
- 如何查找论文资料
- SpringBoot系列(二)--web应用之jsp篇
- Qt--Meta-Object Model
- 计算单词的个数
- RxJava 详解
- Apache Torque实现分页查询
- 一块蛋清皂,把毛孔洗得一干二净
- spring基于xml格式的前后端数据交换的配置 之 MarshallingHttpMessageConverter + xstream
- 在jsp页面可以有三种方式插入java代码
- 51nod 1272 最大距离 (贪心或单调栈)
- Linux下redis安装与使用