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;实际上就是调用这个信号函数罢了

原创粉丝点击