一、Meta-Object簡介
Meta-Object即是Qt的元對象系統,下文都以元對象系統進行描述。在Qt中,具有標志性特征的則是信號和槽函數機制,該機制的背后實現本質上則是元對象系統。編寫Qt代碼的時候,在定義類的時候,需要放置一個Q_OBJECT,為什么呢?后文會描述到,例如如下代碼:
image-20230207220020441
Q_OBJECT本質上是一個宏定義,在進行Qt開發時,所有QObject的派生類都推薦在頭文件中放置Q_OBJECT宏定義,該宏定義如下(出自qobjectdefs.h文件):
#defineQ_OBJECT public: QT_WARNING_PUSH Q_OBJECT_NO_OVERRIDE_WARNING staticconstQMetaObjectstaticMetaObject; virtualconstQMetaObject*metaObject()const; virtualvoid*qt_metacast(constchar*); virtualintqt_metacall(QMetaObject::Call,int,void**); QT_TR_FUNCTIONS private: Q_OBJECT_NO_ATTRIBUTES_WARNING Q_DECL_HIDDEN_STATIC_METACALLstaticvoidqt_static_metacall(QObject*,QMetaObject::Call,int,void**); QT_WARNING_POP structQPrivateSignal{}; QT_ANNOTATE_CLASS(qt_qobject,"")
Qt中,元對象系統包含了支持元對象系統的程序,宏定義,基類,接口函數。這些東西共同構成了Qt的元對象系統:
(1)QObject為想要使用元對象系統的對象提供了基類。
(2)Q_OBJECT宏用于啟動元對象特性,例如:動態屬性、信號和槽函數機制等。
(3)元對象編譯器(moc)為每個QObject子類生成實現元對象特性所需要的代碼。
在實際Qt程序設計中,在派生自QObject的類定義中加上Q_OBJECT后,則可以使用元對象系統所支持的特性了。
二、元對象系統背后機制
如果在派生自QObject的類定義中加上了Q_OBJECT后,在編譯構建過程中,元對象系統的moc工具(本文以Windows平臺為例,該工具則位于具體Qt版本目錄下的bin目錄中)
在Windows命令行下運行,可獲知如下信息:
在QtCreator集成開發環境中,當點擊構建按鈕后,QtCreator會自動調用moc工具,該工具會讀取一個C++源文件,如果它發現一個或多個包含Q_OBJECT宏的類聲明,那么則會生成另外一個C++源文件,源文件中包含每個類的元對象代碼。接著,生成的源文件要么被#include包含到類的源文件中,要么被編譯并鏈接到類的實現中。例如下列一個簡單的項目工程,源碼結構如下:
從上圖可知,工程中包含了一個main.cpp、一個主窗口描述文件mainwindow.cpp/.h、一個stylesheeteditor.cpp/.h文件,由于mainwindow.cpp/.h、stylesheeteditor.cpp/.h支持Qt的元對象系統,在編譯構建過程中,則會生成支持元對象系統的中間文件,如下圖所示:
從上圖可知,這些文件都以moc_xxx方式進行命名,最后結合其他的文件生成了程序可執行體(stylesheet.exe),整個過程可如下圖所示(Windows平臺):
三、再談元對象系統
除了提供對象之間通信的信號和槽函數機制(這是引入該系統的主要原因),元對象系統還提供以下的功能:
(1)Object::metaObject():返回類的關聯元對象。
(2)QMetaObject::className():在運行時以字符串的形式返回類名,而不需要通過C++編譯器提供本地運行時類型信息(RTTI)支持。
(3)QObject::inherits():函數返回一個對象是否是在QObject繼承樹中繼承指定類實例。
(4)QObject::tr()和QObject::trUtf8()為國際化翻譯字符串。
(5)QObject::setProperty()和QObject::property()根據名稱動態設置和獲取屬性。
(6)QMetaObject::newInstance():構造類的新實例。
除了上述所列的功能,還可以使用qobject_cast()對QObject類執行動態強制類型轉換,qobject_cast()函數的行為類似于標準C++ 的dynamic_cast(),它的優點是不需要RTTI支持,并且可以跨動態庫工作。該函數嘗試將其參數轉換為尖括號中指定的指針類型,如果對象的類型正確(在運行時確定),則返回非零指針;如果對象的類型不兼容,則返回nullptr。
例如,假設有一個MyWidget繼承自QWidget,并使用了Q_OBJECT宏聲明,然后使用new創建該實例:
QObject*obj=newMyWidget;
類型為QObject *的obj變量實際上引用了一個MyWidget對象,所以我們可以對它進行適當的類型轉換,如下代碼:
QWidget*widget=qobject_cast(obj);
從QObject到QWidget的轉換是成功的,因為該對象實際上是一個MyWidget,它是QWidget的一個子類。既然知道obj是一個MyWidget,我們也可以將它cast到MyWidget *,如下代碼:
MyWidget*myWidget=qobject_cast(obj);
上述代碼對MyWidget的轉換是也成功的,因為qobject_cast()在內置Qt類型和自定義類型之間沒有區別。
然而對于下列代碼:
QLabel*label=qobject_cast(obj);
對QLabel的強制轉換將失敗,會將指針設置為0。因此可以在運行時處理不同類型的對象,例如:
if(QLabel*label=qobject_cast(obj)) { label->setText(tr("iriczhao")); } elseif(QPushButton*button=qobject_cast (obj)) { button->setText(tr("嵌入式小生")); }
上述代碼使用qobject_case()對obj進行了向QLabel和QPushButton的強制轉換,如果轉換成功,則設置對應的顯示文本。
四、小生總結
在實際Qt開發過程中,雖然可以在沒有Q_OBJECT宏和元對象代碼的情況下將QObject作為基類,但如果沒有使用Q_OBJECT宏,則信號和槽函數機制或在本文中描述的其他特性都不能使用。從元對象系統的角度來看,一個沒有元代碼的QObject子類等價于它最近的有元對象代碼的祖先。這意味著,例如,QMetaObject::className()將不會返回自己類的實際名稱,而是這個祖先的類名稱。
因此,在實際Qt開發過程中,無論實際上是否使用了信號和槽函數機制,都強烈建議QObject的所有子類都使用Q_OBJECT宏。
審核編輯:湯梓紅
-
函數
+關注
關注
3文章
4327瀏覽量
62569 -
代碼
+關注
關注
30文章
4779瀏覽量
68521 -
Meta
+關注
關注
0文章
270瀏覽量
11378 -
編譯器
+關注
關注
1文章
1623瀏覽量
49108 -
Qt
+關注
關注
1文章
302瀏覽量
37899
原文標題:Qt“靈魂”之Meta-Object系統
文章出處:【微信號:嵌入式小生,微信公眾號:嵌入式小生】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論