一、演示代码
test.h
#ifndef TEST_H #define TEST_H #include <QObject> class test : public QObject { Q_OBJECT public: Q_INVOKABLE explicit test(QObject *parent = nullptr); Q_PROPERTY(int a READ f WRITE g) Q_INVOKABLE void t1(); Q_INVOKABLE int t2(const QString &name, QString mark); signals: void sgn1(); int sgn2(int); public slots: void slt1(); int slt2(int); private slots: void slt3(); int slt4(int); private: int f() { return ccc; } void g(int i) { ccc = i; } int ccc; }; #endif // TEST_H
test.cpp
#include "test.h" #include <QDebug> test::test(QObject *parent) : QObject(parent) {} void test::t1() {} int test::t2(const QString &name, QString mark) { qDebug() << name << mark; return 0; } void test::slt1() { qDebug() << "slt1()"; emit sgn1(); } int test::slt2(int) { qDebug() << "slt2()"; return 1; } void test::slt3() { qDebug() << "slt3()"; emit sgn2(3); } int test::slt4(int) { qDebug() << "slt4()"; return 1; }
头文件展开,以及编译后生成:
Q_OBJECT 宏展开
#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 **); \ private: \ static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \ ...
moc_test.cpp
/**************************************************************************** ** Meta object code from reading C++ file 'test.h' ** ** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.9) ** ** WARNING! All changes made in this file will be lost! *****************************************************************************/ #include "../../TestListWidget/test.h" #include <QtCore/qbytearray.h> #include <QtCore/qmetatype.h> #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'test.h' doesn't include <QObject>." #elif Q_MOC_OUTPUT_REVISION != 67 #error "This file was generated using the moc from 5.12.9. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endif QT_BEGIN_MOC_NAMESPACE QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED struct qt_meta_stringdata_test_t { QByteArrayData data[14]; char stringdata0[61]; }; #define QT_MOC_LITERAL(idx, ofs, len) \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ qptrdiff(offsetof(qt_meta_stringdata_test_t, stringdata0) + ofs \ - idx * sizeof(QByteArrayData)) \ ) static const qt_meta_stringdata_test_t qt_meta_stringdata_test = { { QT_MOC_LITERAL(0, 0, 4), // "test" QT_MOC_LITERAL(1, 5, 4), // "sgn1" QT_MOC_LITERAL(2, 10, 0), // "" QT_MOC_LITERAL(3, 11, 4), // "sgn2" QT_MOC_LITERAL(4, 16, 4), // "slt1" QT_MOC_LITERAL(5, 21, 4), // "slt2" QT_MOC_LITERAL(6, 26, 4), // "slt3" QT_MOC_LITERAL(7, 31, 4), // "slt4" QT_MOC_LITERAL(8, 36, 2), // "t1" QT_MOC_LITERAL(9, 39, 2), // "t2" QT_MOC_LITERAL(10, 42, 4), // "name" QT_MOC_LITERAL(11, 47, 4), // "mark" QT_MOC_LITERAL(12, 52, 6), // "parent" QT_MOC_LITERAL(13, 59, 1) // "a" }, "test\0sgn1\0\0sgn2\0slt1\0slt2\0slt3\0slt4\0" "t1\0t2\0name\0mark\0parent\0a" }; #undef QT_MOC_LITERAL static const uint qt_meta_data_test[] = { // content: 8, // revision 0, // classname 0, 0, // classinfo 8, 14, // methods 1, 76, // properties 0, 0, // enums/sets 2, 79, // constructors 0, // flags 2, // signalCount // signals: name, argc, parameters, tag, flags 1, 0, 54, 2, 0x06 /* Public */, 3, 1, 55, 2, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 4, 0, 58, 2, 0x0a /* Public */, 5, 1, 59, 2, 0x0a /* Public */, 6, 0, 62, 2, 0x08 /* Private */, 7, 1, 63, 2, 0x08 /* Private */, // methods: name, argc, parameters, tag, flags 8, 0, 66, 2, 0x02 /* Public */, 9, 2, 67, 2, 0x02 /* Public */, // signals: parameters QMetaType::Void, QMetaType::Int, QMetaType::Int, 2, // slots: parameters QMetaType::Void, QMetaType::Int, QMetaType::Int, 2, QMetaType::Void, QMetaType::Int, QMetaType::Int, 2, // methods: parameters QMetaType::Void, QMetaType::Int, QMetaType::QString, QMetaType::QString, 10, 11, // constructors: parameters 0x80000000 | 2, QMetaType::QObjectStar, 12, 0x80000000 | 2, // properties: name, type, flags 13, QMetaType::Int, 0x00095003, // constructors: name, argc, parameters, tag, flags 0, 1, 72, 2, 0x0e /* Public */, 0, 0, 75, 2, 0x2e /* Public | MethodCloned */, 0 // eod }; void test::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::CreateInstance) { switch (_id) { case 0: { test *_r = new test((*reinterpret_cast< QObject*(*)>(_a[1]))); if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break; case 1: { test *_r = new test(); if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break; default: break; } } else if (_c == QMetaObject::InvokeMetaMethod) { auto *_t = static_cast<test *>(_o); Q_UNUSED(_t) switch (_id) { case 0: _t->sgn1(); break; case 1: { int _r = _t->sgn2((*reinterpret_cast< int(*)>(_a[1]))); if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; case 2: _t->slt1(); break; case 3: { int _r = _t->slt2((*reinterpret_cast< int(*)>(_a[1]))); if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; case 4: _t->slt3(); break; case 5: { int _r = _t->slt4((*reinterpret_cast< int(*)>(_a[1]))); if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; case 6: _t->t1(); break; case 7: { int _r = _t->t2((*reinterpret_cast< const QString(*)>(_a[1])),(*reinterpret_cast< QString(*)>(_a[2]))); if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break; default: ; } } else if (_c == QMetaObject::IndexOfMethod) { int *result = reinterpret_cast<int *>(_a[0]); { using _t = void (test::*)(); if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::sgn1)) { *result = 0; return; } } { using _t = int (test::*)(int ); if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::sgn2)) { *result = 1; return; } } } #ifndef QT_NO_PROPERTIES else if (_c == QMetaObject::ReadProperty) { auto *_t = static_cast<test *>(_o); Q_UNUSED(_t) void *_v = _a[0]; switch (_id) { case 0: *reinterpret_cast< int*>(_v) = _t->f(); break; default: break; } } else if (_c == QMetaObject::WriteProperty) { auto *_t = static_cast<test *>(_o); Q_UNUSED(_t) void *_v = _a[0]; switch (_id) { case 0: _t->g(*reinterpret_cast< int*>(_v)); break; default: break; } } else if (_c == QMetaObject::ResetProperty) { } #endif // QT_NO_PROPERTIES } QT_INIT_METAOBJECT const QMetaObject test::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_test.data, qt_meta_data_test, qt_static_metacall, nullptr, nullptr } }; const QMetaObject *test::metaObject() const { return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; } void *test::qt_metacast(const char *_clname) { if (!_clname) return nullptr; if (!strcmp(_clname, qt_meta_stringdata_test.stringdata0)) return static_cast<void*>(this); return QObject::qt_metacast(_clname); } int test::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 < 8) qt_static_metacall(this, _c, _id, _a); _id -= 8; } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { if (_id < 8) *reinterpret_cast<int*>(_a[0]) = -1; _id -= 8; } #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 0 void test::sgn1() { QMetaObject::activate(this, &staticMetaObject, 0, nullptr); } // SIGNAL 1 int test::sgn2(int _t1) { int _t0{}; void *_a[] = { const_cast<void*>(reinterpret_cast<const void*>(&_t0)), const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; QMetaObject::activate(this, &staticMetaObject, 1, _a); return _t0; } QT_WARNING_POP QT_END_MOC_NAMESPACE
二、QMetaObject
QMetaObject 类描述了 QObject 及其派生类对象的所有元信息,该类是 Qt 元对象系统的核心类,通过该类的成员函数可以获取 QObject 及其派生类对象的所有元信息,
因此可以说 QMetaObject 类的对象是 Qt 中的元对象。
1、获取QMetaObject
QObject中定义了一个虚函数:
virtual const QMetaObject *metaObject() const;
在moc_test.cpp代码,有相应的实现:
const QMetaObject *test::metaObject() const { return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; }
2、staticMetaObject
moc_test.cpp中相应代码为:
QT_INIT_METAOBJECT const QMetaObject test::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_test.data, qt_meta_data_test, qt_static_metacall, nullptr, nullptr } };
QMetaObject结构体定义如下:
struct Q_CORE_EXPORT 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; };
因此可知:superdata指向父类的staticMetaObject,从而形成了对象间的层级链。stringdata指向了qt_meta_stringdata_test.data,data指向了qt_meta_data_test,static_metacall指向了qt_static_metacall。
3、qt_meta_stringdata_test
struct qt_meta_stringdata_test_t { QByteArrayData data[14]; char stringdata0[61]; }; #define QT_MOC_LITERAL(idx, ofs, len) \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ qptrdiff(offsetof(qt_meta_stringdata_test_t, stringdata0) + ofs \ - idx * sizeof(QByteArrayData)) \ ) static const qt_meta_stringdata_test_t qt_meta_stringdata_test = { { QT_MOC_LITERAL(0, 0, 4), // "test" QT_MOC_LITERAL(1, 5, 4), // "sgn1" QT_MOC_LITERAL(2, 10, 0), // "" QT_MOC_LITERAL(3, 11, 4), // "sgn2" QT_MOC_LITERAL(4, 16, 4), // "slt1" QT_MOC_LITERAL(5, 21, 4), // "slt2" QT_MOC_LITERAL(6, 26, 4), // "slt3" QT_MOC_LITERAL(7, 31, 4), // "slt4" QT_MOC_LITERAL(8, 36, 2), // "t1" QT_MOC_LITERAL(9, 39, 2), // "t2" QT_MOC_LITERAL(10, 42, 4), // "name" QT_MOC_LITERAL(11, 47, 4), // "mark" QT_MOC_LITERAL(12, 52, 6), // "parent" QT_MOC_LITERAL(13, 59, 1) // "a" }, "test\0sgn1\0\0sgn2\0slt1\0slt2\0slt3\0slt4\0" "t1\0t2\0name\0mark\0parent\0a" };
由上面可知,其保存了元对象的类名、方法名、信号名、槽名、属性名以及各个参数的名字,这些名字主要用来通过名字反射相应的类、方法、信号、槽以及属性等。
4、qt_meta_data_test
static const uint qt_meta_data_test[] = { // content: 8, // revision 0, // classname 0, 0, // classinfo 8, 14, // methods 1, 76, // properties 0, 0, // enums/sets 2, 79, // constructors 0, // flags 2, // signalCount // signals: name, argc, parameters, tag, flags 1, 0, 54, 2, 0x06 /* Public */, 3, 1, 55, 2, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 4, 0, 58, 2, 0x0a /* Public */, 5, 1, 59, 2, 0x0a /* Public */, 6, 0, 62, 2, 0x08 /* Private */, 7, 1, 63, 2, 0x08 /* Private */, // methods: name, argc, parameters, tag, flags 8, 0, 66, 2, 0x02 /* Public */, 9, 2, 67, 2, 0x02 /* Public */, // signals: parameters QMetaType::Void, QMetaType::Int, QMetaType::Int, 2, // slots: parameters QMetaType::Void, QMetaType::Int, QMetaType::Int, 2, QMetaType::Void, QMetaType::Int, QMetaType::Int, 2, // methods: parameters QMetaType::Void, QMetaType::Int, QMetaType::QString, QMetaType::QString, 10, 11, // constructors: parameters 0x80000000 | 2, QMetaType::QObjectStar, 12, 0x80000000 | 2, // properties: name, type, flags 13, QMetaType::Int, 0x00095003, // constructors: name, argc, parameters, tag, flags 0, 1, 72, 2, 0x0e /* Public */, 0, 0, 75, 2, 0x2e /* Public | MethodCloned */, 0 // eod };
以上描述了类、方法、信号、槽以及属性的一些信息。其前面几个字节对应QMetaObjectPrivate结构体:
struct QMetaObjectPrivate { int revision; int className; int classInfoCount, classInfoData; int methodCount, methodData; int propertyCount, propertyData; int enumeratorCount, enumeratorData; int constructorCount, constructorData; int flags; int signalCount; ... }
qt_meta_stringdata_test和qt_meta_data_test基本存储元对象信息。
三、通过QMetaObject获取classInfo
1、classInfo函数
QMetaClassInfo QMetaObject::classInfo(int index) const { int i = index; // classInfoOffset 求父类的偏移 i -= classInfoOffset(); if (i < 0 && d.superdata) return d.superdata->classInfo(index); QMetaClassInfo result; // priv 把d.data转换成QMetaObjectPrivate对象 if (i >= 0 && i < priv(d.data)->classInfoCount) { result.mobj = this; result.handle = priv(d.data)->classInfoData + 2*i; } return result; }
可见其主要根据QMetaObjectPrivate中信息,来初始化QMetaClassInfo类。
2、QMetaClassInfo
函数classInfo中根据QMetaObjectPrivate信息,初始了QMetaClassInfo中mobj和handle。
const char *QMetaClassInfo::name() const { if (!mobj) return 0; // mobj->d.data[handle] 获取qt_meta_data_test中的数据 // rawStringData 根据index获取qt_meta_stringdata_test.data中字符串 return rawStringData(mobj, mobj->d.data[handle]); } const char* QMetaClassInfo::value() const { if (!mobj) return 0; // mobj->d.data[handle] 获取qt_meta_data_test中的数据 return rawStringData(mobj, mobj->d.data[handle + 1]); } static inline const QByteArray stringData(const QMetaObject *mo, int index) { Q_ASSERT(priv(mo->d.data)->revision >= 7); const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) }; Q_ASSERT(data.ptr->ref.isStatic()); Q_ASSERT(data.ptr->alloc == 0); Q_ASSERT(data.ptr->capacityReserved == 0); Q_ASSERT(data.ptr->size >= 0); return data; } static inline const char *rawStringData(const QMetaObject *mo, int index) { return stringData(mo, index).data(); }
四、通过QMetaObject获取constructor
1、constructor函数
QMetaMethod QMetaObject::constructor(int index) const { int i = index; QMetaMethod result; Q_ASSERT(priv(d.data)->revision >= 2); // priv 把d.data转换成QMetaObjectPrivate对象 // constructorCount 值为2 if (i >= 0 && i < priv(d.data)->constructorCount) { result.mobj = this; // constructorData 值为 79 + 5*i // 当index=0时,其对应qt_meta_data_test中第79个偏移的位置,即 // constructors: name, argc, parameters, tag, flags // 0, 1, 72, 2, 0x0e /* Public */, // 0, 0, 75, 2, 0x2e /* Public | MethodCloned */, result.handle = priv(d.data)->constructorData + 5*i; } return result; }
可见其主要根据QMetaObjectPrivate中信息,来初始化QMetaMethod类。由上分析可知handle指向qt_meta_data_test的偏移地址,其存储了QMetaMethod中所需要的信息。信息为:
// constructors: name, argc, parameters, tag, flags 0, 1, 72, 2, 0x0e /* Public */, 0, 0, 75, 2, 0x2e /* Public | MethodCloned */,
分别对应构造函数的name, argc, parameters, tag, flags信息。
flags值含义如下:
enum MethodFlags { AccessPrivate = 0x00, AccessProtected = 0x01, AccessPublic = 0x02, AccessMask = 0x03, //mask MethodMethod = 0x00, MethodSignal = 0x04, MethodSlot = 0x08, MethodConstructor = 0x0c, MethodTypeMask = 0x0c, MethodCompatibility = 0x10, MethodCloned = 0x20, MethodScriptable = 0x40, MethodRevisioned = 0x80 };
2、QMetaMethod
函数classInfo中根据QMetaObjectPrivate信息,初始了QMetaMethod中mobj和handle。首先我们看下QMetaMethod中的name()函数
2.1 name()
QByteArray QMetaMethod::name() const { if (!mobj) return QByteArray(); // get() 强制转换为QMetaMethodPrivate对象 return QMetaMethodPrivate::get(this)->name(); } QByteArray QMetaMethodPrivate::name() const { return stringData(mobj, mobj->d.data[handle]); } static inline const QByteArray stringData(const QMetaObject *mo, int index) { // d.stringdata指向qt_meta_stringdata_test.data,其是QByteArrayData数组 // QByteArrayDataPtr data中ptr初始化为QByteArrayData数组地址 const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) }; return data; }
stringData函数分析:mo->d.stringdata指向qt_meta_stringdata_test.data,其值为:
struct qt_meta_stringdata_test_t { QByteArrayData data[14]; char stringdata0[61]; }; static const qt_meta_stringdata_test_t qt_meta_stringdata_test = { { QT_MOC_LITERAL(0, 0, 4), // "test" QT_MOC_LITERAL(1, 5, 4), // "sgn1" QT_MOC_LITERAL(2, 10, 0), // "" QT_MOC_LITERAL(3, 11, 4), // "sgn2" QT_MOC_LITERAL(4, 16, 4), // "slt1" QT_MOC_LITERAL(5, 21, 4), // "slt2" QT_MOC_LITERAL(6, 26, 4), // "slt3" QT_MOC_LITERAL(7, 31, 4), // "slt4" QT_MOC_LITERAL(8, 36, 2), // "t1" QT_MOC_LITERAL(9, 39, 2), // "t2" QT_MOC_LITERAL(10, 42, 4), // "name" QT_MOC_LITERAL(11, 47, 4), // "mark" QT_MOC_LITERAL(12, 52, 6), // "parent" QT_MOC_LITERAL(13, 59, 1) // "a" }, "test\0sgn1\0\0sgn2\0slt1\0slt2\0slt3\0slt4\0" "t1\0t2\0name\0mark\0parent\0a" };
即mo->d.stringdata[0]返回QT_MOC_LITERAL(0, 0, 4), // "test",类型为QByteArrayData。
#define QT_MOC_LITERAL(idx, ofs, len) \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ qptrdiff(offsetof(qt_meta_stringdata_test_t, stringdata0) + ofs \ - idx * sizeof(QByteArrayData)) \ ) #define Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \ Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) #define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \ { Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset }
QT_MOC_LITERAL构成了一个QByteArrayData数组,而QByteArrayData被重定义为了QArrayData结构体
typedef QArrayData QByteArrayData; struct Q_CORE_EXPORT QArrayData { QtPrivate::RefCount ref; int size; uint alloc : 31; uint capacityReserved : 1; qptrdiff offset; // in bytes from beginning of header ... }
/即QT_MOC_LITERAL(0, 0, 4), // "test"展开后变成{ Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset } ,相当于初始化QArrayData中的ref,size,alloc,capacityReserved,offset几个成员。
回过头来接着看static inline const QByteArray stringData(const QMetaObject *mo, int index)函数:
static inline const QByteArray stringData(const QMetaObject *mo, int index) { // d.stringdata指向qt_meta_stringdata_test.data,其是QByteArrayData数组 // QByteArrayDataPtr data中ptr初始化为QByteArrayData数组地址 const QByteArrayDataPtr data = { const_cast<QByteArrayData*>(&mo->d.stringdata[index]) }; return data; } struct QByteArrayDataPtr { QByteArrayData *ptr; };
从上可知:QByteArrayDataPtr data中的ptr被初始化成了qt_meta_stringdata_test.data数组地址。并且在返回时转换成了QByteArray对象。那么QByteArrayDataPtr又是如何转换成QByteArray的呢
我们跟踪QByteArray代码,其提供了一个构造函数:
typedef QTypedArrayData<char> Data; inline QByteArray(QByteArrayDataPtr dd) : d(static_cast<Data *>(dd.ptr)) { } template <class T> struct QTypedArrayData : QArrayData { ... }
其调用此构造函数转换成QByteArray,从代码中可知其把dd.ptr转换成了Data结构体,而Data是QTypedArrayData重定义。QTypedArrayData是一个继承QArrayData的模板。QByteArrayDataPtr dd成员变量ptr类型正好是QArrayData。从而整个对应起来了。QArrayData和QTypedArrayData的代码如下:
typedef QArrayData QByteArrayData; struct Q_CORE_EXPORT QArrayData { QtPrivate::RefCount ref; int size; uint alloc : 31; uint capacityReserved : 1; qptrdiff offset; // in bytes from beginning of header void *data() { Q_ASSERT(size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData)); return reinterpret_cast<char *>(this) + offset; } ... } typedef QTypedArrayData<char> Data; inline QByteArray(QByteArrayDataPtr dd) : d(static_cast<Data *>(dd.ptr)) { } template <class T> struct QTypedArrayData : QArrayData { ... }
2.2 QMetaMethod::access()
QMetaMethod::Access QMetaMethod::access() const { if (!mobj) return Private; // constructorData 值为 79 + 5*i // 当index=0时,其对应qt_meta_data_test中第79个偏移的位置,即 // constructors: name, argc, parameters, tag, flags // 0, 1, 72, 2, 0x0e /* Public */, // 0, 0, 75, 2, 0x2e /* Public | MethodCloned */ // 即mobj->d.data[handle + 4]值为0x0e return (QMetaMethod::Access)(mobj->d.data[handle + 4] & AccessMask); }
(mobj->d.data[handle + 4] 值为0x0e,flags代表的含义如下:
enum MethodFlags { AccessPrivate = 0x00, AccessProtected = 0x01, AccessPublic = 0x02, AccessMask = 0x03, //mask MethodMethod = 0x00, MethodSignal = 0x04, MethodSlot = 0x08, MethodConstructor = 0x0c, MethodTypeMask = 0x0c, MethodCompatibility = 0x10, MethodCloned = 0x20, MethodScriptable = 0x40, MethodRevisioned = 0x80 };
0x0e为0b0000,1110,可知其访问权限为public,方法类型为constructor。依次类推可以获取访问权限与方法类型,以及方法修订版本(对应方法QMetaMethod::revision(),QMetaMethod::methodType())。
2.3 QMetaMethod::tag()
const char *QMetaMethod::tag() const { if (!mobj) return 0; return QMetaMethodPrivate::get(this)->tag().constData(); } QByteArray QMetaMethodPrivate::tag() const { Q_ASSERT(priv(mobj->d.data)->revision >= 7); // constructors: name, argc, parameters, tag, flags // 0, 1, 72, 2, 0x0e /* Public */, // 对应值为2,指向第2个字符串,值为空 return stringData(mobj, mobj->d.data[handle + 3]); }
这个函数可用于注解。其实现示例如:
定义tag,用于注解
// In the class MainWindow declaration #ifndef Q_MOC_RUN // define the tag text as empty, so the compiler doesn't see it # define MY_CUSTOM_TAG #endif ... private slots: MY_CUSTOM_TAG void testFunc();
获取定义的tag,并可解释成特殊用途:
MainWindow win; win.show(); int functionIndex = win.metaObject()->indexOfSlot("testFunc()"); QMetaMethod mm = win.metaObject()->method(functionIndex); qDebug() << mm.tag(); // prints MY_CUSTOM_TAG
从源码角度分析Qt元对象系统2:https://developer.aliyun.com/article/1597143