作者:billy
著作權宣告:著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處
什么是 Qt 元物件系統
Qt 的元物件系統(Meta-Object System)提供了物件之間通信的信號與槽機制、運行時型別資訊和動態屬性系統,
元物件系統由以下三個基礎組成:
- QObject 類,是所有使用元物件系統的類的基類,換句話說只有繼承 QObject 才能使用元物件系統;
- Q_OBJECT 宏,在一個類的 private 部分宣告 ,使得類可以使用元物件的特性,如動態屬性、信號與槽;
- MOC(元物件編譯器),為每個 QObject 的子類提供必要的代碼來實作元物件系統的特性,構建專案時,MOC 工具讀取 C++ 源檔案,當它發現類的定義里有 Q_OBJECT 宏時,它就會為這個類生成另外一個包含有元物件支持代碼的 C++ 源檔案,這個生成的源檔案連同類的實作檔案一起被編譯和連接,通常這個新的C++原檔案會再以前的C++原檔案前面加上moc_作為新的檔案名;
元物件系統的功能
除了提供在物件間通訊的機制外,元物件系統還包含以下幾種功能:
QObject::metaObject()方法,獲得與一個類相關聯的meta-object;QMetaObject::className()方法,在運行期間回傳一個物件的類名,不需要本地C++編譯器的 RTTI(run time type information)支持;QObject::inherits()方法,用來判斷一個物件的類是不是從一個特定的類繼承而來;QObject::tr()、QObject::trUtf8()方法,為軟體的國際化翻譯字串;QObject::setProperty()、QObject::property()方法,根據屬性名動態的設定和獲取屬性值;QMetaObject::newInstance()方法,構造類的新實體;- 使用
qobject_cast()方法可以在在 QObject 類之間提供動態轉換,qobject_cast()方法的功能類似于標準 C++ 的 dynamic_cast(),但是 qobject_cast() 不需要RTTI的支持,在一個 QObject 類或者它的派生類中,如果不定義 Q_OBJECT 宏,那么這些功能將不能被使用,從 meta-object 的觀點來看,一個沒有定義 Q_OBJECT 宏的類與它最接近的那個祖先類是相同的,那么 QMetaObject::className() 方法回傳的名字并不是該類的名字,而是與它最近接的那個祖先類的名字,所以,任何從 QObject 繼承的類都必須定義 Q_OBJECT 宏,
Meta Object 的所有資料和方法都封裝在 QMetaObject 類中,它包含一個 Qt 類的 meta 資訊,并且提供查詢功能,meta 資訊包含:
- 信號表(signal table),與對應 Qt 類相關的系統定義及自定義的 signal 的名字;
- 槽表(slot table),與對應 Qt 類相關的系統定義及自定義的 slot 的名字;
- 類資訊表(class info table),Qt 類的型別資訊;
- 屬性表(property table),與對應類中的所有屬性的名字;
- 指向 parent meta object 的指標;
Q_OBJECT 宏的定義
Q_OBJECT 定義在 /src/corelib/kernel/Qobjectdefs.h 檔案中,Q_OBJECT 宏的定義如下:
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
static const QMetaObject staticMetaObject; \
Q_OBJECT_GETSTATICMETAOBJECT \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
QT_TR_FUNCTIONS \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
QMetaObject 型別的靜態成員變數 staticMetaObject 是元資料的資料結構,metaObject,qt_metacast,qt_metacall、qt_static_metacall 四個虛函式由 MOC 在生成的 moc_xxx.cpp 檔案中實作,
1)metaObject 的作用是得到元資料表指標;
2)qt_metacast 的作用是根據簽名得到相關結構的指標,回傳void*指標;
3)qt_metacall 的作用是查表然后呼叫呼叫相關的函式;
4)qt_static_metacall 的作用是呼叫元方法(信號和槽);
元物件編譯器 MOC
- MOC 的功能
1)處理 Q_OBJECT 宏和 signals/slots 關鍵字,生成信號和槽的底層代碼;
2)處理 Q_PROPERTY() 和 Q_ENUM() 生成 property 系統代碼;
3)處理 Q_FLAGS() 和 Q_CLASSINFO() 生成額外的類 meta 資訊;
4)不需要 MOC 處理的代碼可以用預定義的宏括起來,如下:
#ifndef Q_MOC_RUN
…
#endif
-
MOC 的限制
1)模板類不能使用信號/槽機制;
2)MOC不擴展宏,所以信號和槽的定義不能使用宏, 包括 connect 的時候也不能用宏做信號和槽的名字以及引數;
3)從多個類派生時,QObject 派生類必須放在第一個, QObject(或其子類)作為多重繼承的父類之一時,需要把它放在第一個, 如果使用多重繼承,MOC 在處理時假設首先繼承的類是 QObject 的一個子類,需要確保首先繼承的類是 QObject 或其子類;
4)函式指標不能作為信號或槽的引數, 因為其格式比較復雜,MOC 不能處理,可以用typedef 把它定義成簡單的形式再使用;
5)用列舉型別或 typedef 的型別做信號和槽的引數時,必須 fully qualified,這個詞中文不知道怎么翻譯才合適,簡單的說就是,如果是在類里定義的,必須把類的路徑或者命名空間的路徑都加上, 防止出現混淆,如 Qt::Alignment 之類的,前面的 Qt 就是 Alignment 的 qualifier;
6)信號和槽不能回傳參考型別;
7)signals 和 slots 關鍵字區域只能放置信號和槽的定義,不能放其它的如變數、建構式的定義等,友元宣告不能位于信號或者槽宣告區內;
8)嵌套類不能含有信號和槽 ; -
自定義型別的注冊
Qt 執行緒間傳遞自定義型別資料時,自己定義的型別如果直接使用信號槽來傳遞的話會產生下面這種錯誤:
QObject::connect: Cannot queue arguments of type 'XXXXX' (Make sure 'XXXXX' is registed using qRegisterMetaType().)
錯誤原因:當一個 signal 被放到佇列中(queued)時,引數(arguments)也會被一起放到佇列中,引數在被傳送到 slot 之前需要被拷貝、存盤在佇列中;為了能夠在佇列中存盤引數,Qt 需要去 construct、destruct、copy 引數物件,而為了讓 Qt 知道怎樣去作這些事情,引數的型別需要使用 qRegisterMetaType 來注冊;
注冊自定義類的步驟如下:
1)自定義型別時在類的頂部包含:#include ;
2)在型別定義完成后,加入宣告:Q_DECLARE_METATYPE(XXX);
3)在 main() 函式中注冊自定義型別別:qRegisterMetaType(“XXX”),如果希望使用型別的參考,同樣要注冊:qRegisterMetaType(“XXX&”);
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/282708.html
標籤:其他
