Qt學習 -- Meta_Object Model系統

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::metaObject()返回類關聯的meta-object對象。
  • QMetaObject::className()在運行時以字符串的形式返回類名,無需C++編譯器提供運行時類別信息(RTTI)的支持。
  • QObject::inherits()返回一個對象是否是QObject繼承樹上一個類的實例。
  • QObject::tr()和QObject::trUtf8()提供國際化支持,將字符串翻譯成指定的語言。
  • QObject::setProperty()和QObject::property()通過名稱動態設置和獲取屬性。
  • QMetaObject::newInstance()構造類的一個新實例。

類型轉換

除此之外,還可以用qobject_cast()動態轉換QObject類的類型。qobject_cast()函數和標準C++的dynamic_cast()功能類似,它的優點在於:不需要RTTI的支持,而且可以跨越動態連接庫的轉換。它嘗試將它的參數轉換成尖括號內的指針類型,如果對象是正確的類型(在運行時檢查),則返回非零指針;否則,返回0,說明對象類型不兼容。

例如,假設MyWidget繼承自QWidget,同時也聲明瞭Q_OBJECT宏。

QObject *obj = new MyWidget;

QObject *類型的變量obj實際上指向一個MyWidget對象,因此,我們可以適當地進行類型轉換:

QWidget *widget = qobject_cast<QWidget *>(obj);

因爲obj實際上是一個MyWidget,而MyWidget是QWidget的子類,所以,從QObject轉換爲QWidget成功了。既然知道了obj是MyWidget類型的,那麼我們也可以將其轉換爲MyWidget *:

MyWidget *myWidget = qobject_cast<MyWidget *>(obj);

到MyWidget類型的轉換也是成功的,因爲qobject_cast()並不區分內建的Qt類型和自定義類型。可是,轉換到QLabel卻失敗了,返回的指針爲0。

QLabel *label = qobject_cast<QLabel *>(obj);// label0

QMetaObject

每個QObject類都有一個對應的QMetaObject類,形成一個平行的類型層次。

QMetaObject提供的信息:
下面通過QMetaObject的接口來解讀QMetaObject提供的信息:

1、基本信息

const char * className () constconst 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信息的臃腫。

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 宏就不存在了。

總結

至此,我們應該是對Qt Meta-Object Model有了一個清楚的認識

  • Qt提供了一個QObject的基類
  • Qt擴展了C++語法,提供了Q_OBJECT、Q_INVOKABLE、signals、slots、emit、SIGNAL,SLOT、Q_PROPERT、Q_ENUM、Q_FLAG、Q_CLASSINFO等宏,moc會識別這些宏並生成對應的moc源代碼
  • Q_OBJECT宏中聲明瞭QMetaObject的靜態對象,QMetaObject中記錄了classinfo、method、property、enum,並提供了metacall來操作
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章