元對象系統
Qt 元對象系統(Meta-Object System)
Qt的元對象系統基於如下三件事情:
1. 類:QObject,為所有需要利用元對象系統的對象提供了一個基類。
2. 宏:Q_OBJECT,通常可以聲明在類的私有段中,讓該類可以使用元對象對象的特性,比如動態屬性,信號和槽。
3. 編譯器:元對象編譯器(moc)為每個QObject子對象自動生成必要的代碼來實現元對象特性。
moc工具會讀入C++的源檔案,如果它發現了一個或者多個聲明了Q_OBJECT宏的類,它就創建另一個C++源檔案,為每個類生成包含元對象實現的代碼。這些編譯生成的源檔案通常都 已經被包含到類的源檔案中或者和類的實現同時被編譯和連結。
除了為對象間的通信提供信號和槽(signals and slots)機制之外,元對象的代碼還提供下列特性:
· 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的支持,而且可以跨越動態連線庫的邊界。它嘗試將它的參數cast成尖括弧內的對象類型,如果對象是正確的類型(運行時決定)則返回非零,否則返回0,說明對象類型不兼容。
例如,假設MyWidget繼承自QWidget,同時也聲明了Q_OBJECT宏,
QObject *obj = new MyWidget;QObject類型的變數obj實際上指向一個MyWidget對象,因此我們可以這樣進行類型轉換:
QWidget *widget = qobject_cast<QWidget*> (obj);到MyWidget的轉型可以成功是因為qobject_cast()並沒有對Qt內建對象和定製的擴展對象分別對待。
QLABEL *label = qobject_cast<QLabel *>(obj); // label is 0另一方面到QLabel的轉型則會失敗,指針會被設定為0。這樣使得我們可以在運行時根據對象類型,對不同類型的對象進行不同的處理:
if (QLabel *label = qobject_cast<QLabel *>(obj))
{ label->setText(tr("Ping")); }
else if (QPushButton *button = qobject_cast<QPushButton *>(obj))
{ button->setText(tr("Pong!")); }
儘管我們可以在不用Q_OBJECT宏和原對象信息的情況下仍舊使用QObject作為基類,但是像信號和槽以及其他這裡描述的特性將無法使用。從元對象系統的觀點來看,一個沒有元對象代碼的QObject子類和其最接近的有元對象代碼的祖先是等同的。這也就意味著,QMetaObject::className()將不會返回你的類的真實的名字,而是該類某一個祖先的名字。
因此,我們強烈建議所有QObject的子類都是用Q_OBJECT宏,不管你實際上是否使用信號和槽,以及屬性