[QT入門篇]4 QT的元對象系統

QT對C++進行了擴展,提供了三個主要的功能:信號槽、運行時類型信息和動態屬性,這三個擴展功能都是由“元對象系統”提供的。

元對象系統基於三個支撐點:

1 OObject爲需要使用元對象系統有點的類提供了基類。

2 Q_OBJECT宏聲明在類的私有段中,可用來啓用元對象特徵,如動態屬性,信號槽。

3 元對象編譯器(moc)爲每一個QObject子類提供了實現元對象特徵的必要代碼。

MOC工具讀取C++源代碼。如果它發現一個或者多個類的聲明包括了宏Q_OBJECT,它產生另一個C++源代碼文件,這個文件中包含了含有宏Q_OBJCET類的元對象代碼。這個新產生的源文件或者被包含值類的源文件中或者,或者更通常的是被編譯和鏈接到類的的實現中。

元對象系統除了提供信號和槽機制(介紹元對象系統的主要原因),還提供如下特徵:

1 QObject::metaObject()返回了類關聯的元對象;

2 QMetaObjcet::className()在運行時返回字符串形式的類名稱,不需要通過C++編譯器的原始運行時類型信息支持。

3 QObject::inherits()方法返回一個對象是否是QObject類或者QObject子類的實例。

4 QObject::tr()和Qobject::trUtf8()用來完成國際化;

5 QObject::SetProperty()和QObject::property()通過名稱動態的設置和獲取屬性;

6 QMetaObject::newInstance()構造類的新實例。

對QObject類也可以使用動態轉換qobject_cast(),qobject_cast()函數與標準C++dynamic_cast()的行爲很像,優點是不需要RTTI支持,並且它可以跨動態庫邊界。qobject_cast()嘗試將它的參數轉換到特定的指針類型,如果對象是正確的類型(在運行時判斷)返回非0指針,如果不兼容則返回0。

看下面的例子。我們假設MyWidget繼承了Qwidget並且聲明瞭宏Q_OBJECT:

QObject *obj = new MyWidget;

變量Obj是QObject類型,實際引用到一個MyWidget對象,所以我們可以轉換:

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

從QObject到QWidget的轉換是成功的,因爲obj實際上就是一個MyWidget,是Qwidget的子類。現在我們知道obj是一個MyWidget對象,我們可以轉換到MyWidget *:

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

到MyWidget的轉換也是成功的,因爲qobject_cast()對待QT內建類型和自定義類型之間沒有區別的。

下面的轉換則是失敗的:

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

obj到Qlabel的轉換是失敗的。label也被設置爲0。

這種運行時類型信息機制可以在運行時處理不同類型的對象,比如:

 if (QLabel *label = qobject_cast<QLabel *>(obj))
 {
        label->setText(tr("Ping"));
    } 
else if (QPushButton *button = qobject_cast<QPushButton *>(obj)) {
        button->setText(tr("Pong!"));
}

當然也可以使用QObject做爲基類卻不使用Q_OBJECT宏,這樣的類就沒有了元對象代碼,前文提到的信號槽和其他特徵也就都失效了。從元對象系統的觀點來看,一個不使用元代碼QObject子類等效於它最近的使用元對象代碼的祖先。這就意味着,QMetaObject::className() 將會返回祖先的類名而不是實際類的名字。

因此強烈建議大家,所有QObject的子類都使用Q_OBJECT宏,無論是否使用了信號槽和動態屬性。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章