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 ) const
int enumeratorCount () const
int enumeratorOffset () const
int indexOfEnumerator ( const char * name ) const
5、method:描述類中所包含方法信息:包括property,signal,slot等,包括祖先類,如何組織暫時不確定。
QMetaMethod method ( int index ) const
int methodCount () const
int methodOffset () const
int indexOfMethod ( const char * method ) const
int indexOfSignal ( const char * signal ) const
int 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信息的臃腫。
下面是Q_OBJECT的宏定義
#define 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_TR_FUNCTIONS \ private: \ Q_OBJECT_NO_ATTRIBUTES_WARNING \ Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \ QT_WARNING_POP \ struct QPrivateSignal {}; \ QT_ANNOTATE_CLASS(qt_qobject, "")
因此:
每一個定義了Q_OBJECT宏的,直接或者間接繼承於QObject的類 有包含一個名為staticMetaObject的靜態QMetaObject對象, 一個私有的靜態函數qt_static_metacall。
它們為該類型的所有對象共有,同時它也繼承了每一個祖父對象這兩個靜態成員和函數。
virtual const QMetaObject *metaObject() const; \ 用於獲取類靜態擁有的元對象
virtual void *qt_metacast(const char *); \ 通過元對象獲取對象指針
virtual int qt_metacall(QMetaObject::Call, int, void **); \ 用於信號槽機制
每個不同的類 在MOC生成代碼的時候會重寫寫這些虛函數。
QMetaObjectl類的定義
struct Q_CORE_EXPORT QMetaObject { class Connection; const char *className() const; const QMetaObject *superClass() const; bool inherits(const QMetaObject *metaObject) const Q_DECL_NOEXCEPT; QObject *cast(QObject *obj) const; const QObject *cast(const QObject *obj) const; #ifndef QT_NO_TRANSLATION QString tr(const char *s, const char *c, int n = -1) const; #endif // QT_NO_TRANSLATION int methodOffset() const; int enumeratorOffset() const; int propertyOffset() const; int classInfoOffset() const; int constructorCount() const; int methodCount() const; int enumeratorCount() const; int propertyCount() const; int classInfoCount() const; int indexOfConstructor(const char *constructor) const; int indexOfMethod(const char *method) const; int indexOfSignal(const char *signal) const; int indexOfSlot(const char *slot) const; int indexOfEnumerator(const char *name) const; int indexOfProperty(const char *name) const; int indexOfClassInfo(const char *name) const; QMetaMethod constructor(int index) const; QMetaMethod method(int index) const; QMetaEnum enumerator(int index) const; QMetaProperty property(int index) const; QMetaClassInfo classInfo(int index) const; QMetaProperty userProperty() const; static bool checkConnectArgs(const char *signal, const char *method); static bool checkConnectArgs(const QMetaMethod &signal, const QMetaMethod &method); static QByteArray normalizedSignature(const char *method); static QByteArray normalizedType(const char *type); // internal index-based connect static Connection connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type = 0, int *types = Q_NULLPTR); // internal index-based disconnect static bool disconnect(const QObject *sender, int signal_index, const QObject *receiver, int method_index); static bool disconnectOne(const QObject *sender, int signal_index, const QObject *receiver, int method_index); // internal slot-name based connect static void connectSlotsByName(QObject *o); // internal index-based signal activation static void activate(QObject *sender, int signal_index, void **argv); static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv); static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv); static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(Q_NULLPTR), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()); static inline bool invokeMethod(QObject *obj, const char *member, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(Q_NULLPTR), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) { return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } static inline bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericArgument val0 = QGenericArgument(Q_NULLPTR), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) { return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } static inline bool invokeMethod(QObject *obj, const char *member, QGenericArgument val0 = QGenericArgument(Q_NULLPTR), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) { return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } QObject *newInstance(QGenericArgument val0 = QGenericArgument(Q_NULLPTR), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const; enum Call { InvokeMetaMethod, ReadProperty, WriteProperty, ResetProperty, QueryPropertyDesignable, QueryPropertyScriptable, QueryPropertyStored, QueryPropertyEditable, QueryPropertyUser, CreateInstance, IndexOfMethod, RegisterPropertyMetaType, RegisterMethodArgumentMetaType }; int static_metacall(Call, int, void **) const; static int metacall(QObject *, Call, int, void **); 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; };
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;
元對象 的所有數據都由該結構定義,
1、const QMetaObject *superdata; 父類的staticMetaObject指針
2、QByteArrayData *stringdata; 字符串數組,保存類的 類名,槽函數名 信號函數名等 字符串信息。
3、const uint *data; 無符號整形數組,該數組是個預定義的復合數據結構,由QMetaObjectPrivate 類提供管理,保存了類的基本信息,和一些索引值
4、const QMetaObject * const *relatedMetaObjects; 和void *extradata; MOC不會為他們生成對應的數據
MOC 為一個類生成元數據例子。類widget 包含三個信號 和 槽,分別為
protected slots:
void TestSlot(QString &str);
signals:
void TestSignal1(QString &str);
void TestSignal2(QString &str,int i);
void TestSignal3();
MOC生成的代碼
QT_BEGIN_MOC_NAMESPACE struct qt_meta_stringdata_Widget_t { QByteArrayData data[9]; char stringdata0[68]; }; #define QT_MOC_LITERAL(idx, ofs, len) \ Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ qptrdiff(offsetof(qt_meta_stringdata_Widget_t, stringdata0) + ofs \ - idx * sizeof(QByteArrayData)) \ ) static const qt_meta_stringdata_Widget_t qt_meta_stringdata_Widget = { { //對應QMetaObject的stringdata ,QT_MOC_LITERAL宏來構成QByteArray對象,每個QByteArray對 QT_MOC_LITERAL(0, 0, 6), // "Widget" 應一個下面\0結尾的字符串,以此方便對字符串數據的管理 QT_MOC_LITERAL(1, 7, 11), // "TestSignal1" QT_MOC_LITERAL(2, 19, 0), // "" QT_MOC_LITERAL(3, 20, 8), // "QString&" QT_MOC_LITERAL(4, 29, 3), // "str" QT_MOC_LITERAL(5, 33, 11), // "TestSignal2" QT_MOC_LITERAL(6, 45, 1), // "i" QT_MOC_LITERAL(7, 47, 11), // "TestSignal3" QT_MOC_LITERAL(8, 59, 8) // "TestSlot" }, "Widget\0TestSignal1\0\0QString&\0str\0" "TestSignal2\0i\0TestSignal3\0TestSlot" }; #undef QT_MOC_LITERAL static const uint qt_meta_data_Widget[] = {
說明:QT_MOC_LITERAL(0, 0, 6), // "Widget" (0:序號,0,:字符("Widget\0T...")在字符串中的起始位置,6:字符長度)。
// content: 7, // revision 0, // classname 0, 0, // classinfo 4, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 3, // signalCount // signals: name, argc, parameters, tag, flags //其中第一值是QByteArrayData數組的索引值,以此來找到對應的字符串名稱,第二個值是參數個數,第三個是參數 1, 1, 34, 2, 0x06 /* Public */, //的大小 5, 2, 37, 2, 0x06 /* Public */, 7, 0, 42, 2, 0x06 /* Public */, // slots: name, argc, parameters, tag, flags 8, 1, 43, 2, 0x09 /* Protected */, // signals: parameters QMetaType::Void, 0x80000000 | 3, 4, QMetaType::Void, 0x80000000 | 3, QMetaType::Int, 4, 6, QMetaType::Void, // slots: parameters QMetaType::Void, 0x80000000 | 3, 4, 0 // eod }; void Widget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) //以名稱或索引方式調用對應的信號函數 { if (_c == QMetaObject::InvokeMetaMethod) { Widget *_t = static_cast<Widget *>(_o); Q_UNUSED(_t) switch (_id) { case 0: _t->TestSignal1((*reinterpret_cast< QString(*)>(_a[1]))); break; case 1: _t->TestSignal2((*reinterpret_cast< QString(*)>(_a[1])),(*reinterpret_cast< int(*)>(_a[2]))); break; case 2: _t->TestSignal3(); break; case 3: _t->TestSlot((*reinterpret_cast< QString(*)>(_a[1]))); break; default: ; } } else if (_c == QMetaObject::IndexOfMethod) { int *result = reinterpret_cast<int *>(_a[0]); void **func = reinterpret_cast<void **>(_a[1]); { typedef void (Widget::*_t)(QString & ); if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Widget::TestSignal1)) { *result = 0; return; } } { typedef void (Widget::*_t)(QString & , int ); if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Widget::TestSignal2)) { *result = 1; return; } } { typedef void (Widget::*_t)(); if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Widget::TestSignal3)) { *result = 2; return; } } } } const QMetaObject Widget::staticMetaObject = { { &QWidget::staticMetaObject, qt_meta_stringdata_Widget.data, qt_meta_data_Widget, qt_static_metacall, Q_NULLPTR, Q_NULLPTR} //初始化靜態對象staticMetaObject }; const QMetaObject *Widget::metaObject() const { return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; } void *Widget::qt_metacast(const char *_clname) { if (!_clname) return Q_NULLPTR; if (!strcmp(_clname, qt_meta_stringdata_Widget.stringdata0)) return static_cast<void*>(const_cast< Widget*>(this)); return QWidget::qt_metacast(_clname); } int Widget::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QWidget::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { if (_id < 4) qt_static_metacall(this, _c, _id, _a); _id -= 4; } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { if (_id < 4) *reinterpret_cast<int*>(_a[0]) = -1; _id -= 4; } return _id; } // SIGNAL 0 void Widget::TestSignal1(QString & _t1) { void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; QMetaObject::activate(this, &staticMetaObject, 0, _a); } // SIGNAL 1 void Widget::TestSignal2(QString & _t1, int _t2) { void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)) }; QMetaObject::activate(this, &staticMetaObject, 1, _a); } // SIGNAL 2 void Widget::TestSignal3() { QMetaObject::activate(this, &staticMetaObject, 2, Q_NULLPTR); } QT_END_MOC_NAMESPACE
其中 static const uint qt_meta_data_Widget[] = {
// content:
7, // revision
0, // classname
0, 0, // classinfo
4, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
3, // signalCount
對應
struct QMetaObjectPrivate
{
enum { OutputRevision = 7 }; // Used by moc, qmetaobjectbuilder and qdbus
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData; //since revision 2
int flags; //since revision 3
int signalCount; //since revision 4
說明: 7, // revision,就是將QMetaObjectPrivate中的revision復制為7.
