读QT5.7源码(三)Q_OBJECT 和QMetaObject
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的宏定義
?
因此:
每一個定義了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_TRANSLATIONQString tr(const char *s, const char *c, int n = -1) const; #endif // QT_NO_TRANSLATIONint 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 connectstatic Connection connect(const QObject *sender, int signal_index,const QObject *receiver, int method_index,int type = 0, int *types = Q_NULLPTR);// internal index-based disconnectstatic 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 connectstatic void connectSlotsByName(QObject *o);// internal index-based signal activationstatic 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 dataconst 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 dataconst 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_LITERALstatic const uint qt_meta_data_Widget[] = {? ?說明:QT_MOC_LITERAL(0, 0, 6), // "Widget"? (0:序號,0,:字符("Widget\0T...")在字符串中的起始位置,6:字符長度)。
?
// content:7, // revision0, // classname0, 0, // classinfo4, 14, // methods0, 0, // properties0, 0, // enums/sets0, 0, // constructors0, // flags3, // 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, flags8, 1, 43, 2, 0x09 /* Protected */,// signals: parametersQMetaType::Void, 0x80000000 | 3, 4,QMetaType::Void, 0x80000000 | 3, QMetaType::Int, 4, 6,QMetaType::Void,// slots: parametersQMetaType::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.
轉載于:https://www.cnblogs.com/senior-engineer/p/9837379.html
總結
以上是生活随笔為你收集整理的读QT5.7源码(三)Q_OBJECT 和QMetaObject的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 耗时 2 年,用 8.5 万块乐高积木最
- 下一篇: vue-cli eslint 规则