1. 程式人生 > >Qt元對象系統簡介

Qt元對象系統簡介

鏈接 alsa setprop 自動生成 包含 log 工具 代碼 智能

Qt中提供了c++的擴展,提供了一種元對象系統的機制,(meta-object-system)的機制。其中包含了信號與槽的內部機制,能夠訪問到QObject子類的元對象信息的功能。

Q_OBJECT 宏聲明了在每一個QObject子類中必須首選的內省函數:metaObject(),tr(),qt_metacall()以及其他一些函數

Qtmoc工具生成了用於Q_OBJECT聲明的所有函數和所有信號的實現

同時也提供了connect()和disconnect()這樣的內省函數

Qt 不使用標準的C++語言,而是進行了一定程度的擴展,增加了一些新的關鍵字(例如 signals、slots、emit 等),並實現了反射(內省)機制。

我們知道,

C++的對象內存模型非常幹凈,只有成員變量和成員函數,沒有保留額外的類型信息,這使得C++非常高效。所謂類型信息,就是對象所屬的類、所包含的成員函數和成員變量(以及它們的修飾符)、所在的繼承關系等。類型信息用來描述一個對象的各種屬性。

雖然C++提供了RTTI機制,但非常簡陋,對象被創建後僅能獲得類的名字,這使得RTTI不堪重用,如同雞肋。像Java、C#等面向對象的語言,可以通過對象完整還原出類的信息,例如類的名字、父類、類所包含的成員變量和成員函數以及它們的修飾符等。這被稱作反射機制或內省機制,也就是說對象可以認知自己。

反射機制無疑會增加額外的存儲空間,在效率上有所犧牲,而且在普通程序中沒有用武之地,為了保持幾乎與
C同等的效率,C++不提供反射機制也有一定的道理。

但對於大型框架或類庫來說,反射機制有時很有必要,它會增加程序的靈活性和動態性,例如動態加載類、解耦等。最明顯的一個例子是編譯器的智能提示,當輸入完對象的名稱,再輸入.->,就會提示該對象擁有的變量和函數,這是反射機制的典型應用。

如今,C++的反射機制在 Qt、Boost 等框架中的實現已經非常成熟。

moc

Qt 在將源代碼交給標準C++編譯器之前,例如GCC、VS等,需要提前將這些擴展的語法去除掉。完成這一操作的就是 moc。

moc 全稱是"Meta-Object Compiler",也就是”元對象編譯器“。moc 就是一個源代碼分析程序,它會讀取C++源文件,如果發現在一個頭文件中包含了宏 Q_OBJECT,則會生成另外一個C++源文件。這個源文件中包含了 Q_OBJECT 宏的實現代碼。這個新文件的名字將由源文件名加上moc_前綴構成,讀者可以在

Debug 或 Release 目錄中找到。

新文件並不會替換舊文件,而是與舊文件一起進入編譯系統,最終被鏈接到二進制代碼中去。

可以發現,moc 的執行在C++預處理器之前,因為預處理器執行之後會進行宏替換,Q_OBJECT 就不存在了。

元對象系統

Qt中的元對象系統是用來處理對象間通訊的信號/槽機制、運行時的類型信息和 動態屬性系統。

它基於下列三類:

  1. QObject類;為所有需要利用原對象系統的對象提供了一個基類。
  2. 類聲明中的私有段中的Q_OBJECT宏;通常可以聲明在類的私有段中,讓該類可以使用元對象的特性,比如動態屬性,信號和槽。
  3. 元對象編譯器(moc元對象編譯器(moc)為每個QObject子對象自動生成必要的代碼來實現元對象特性。

moc讀取C++源文件。如果它發現其中包 含一個或多個類的聲明中含有Q_OBJECT宏,它就會給含有Q_OBJECT宏的類生成另一個 含有元對象代碼的C++源文件。這個生成的源文件可以被類的源文件包含(#include) 到或者和這個類的實現一起編譯和連接。

除了提供對象間通訊的信號和槽機制之 外(介紹這個系統的主要原因),QObject中的元對象代碼還提供以下特征:

  • className()函數在運行的時候以 字符串返回類的名稱,不需要C++編譯器中的本地運行類型信息(RTTI)的支持。
  • inherits()函數返回這個對象是否 是一個繼承於QObject繼承樹中一個特定類的類的實例。
  • tr()trUtf8() 兩個函數是用於國際化中的字符串翻譯。
  • setProperty()property()兩個函數是用來通過名稱動態設置和 獲得對象屬性的。
  • metaObject()函數返回這個類 所關聯的元對象

雖然你使用QObject作為一個基類而不使用Q_OBJECT宏和元對象代碼是可以的, 但是如果Q_OBJECT宏沒有被使用,那麽這裏的信號和槽以及其它特征描述都不會被 提供。根據元對象系統的觀點,一個沒有元代碼的QObject的子類和它含有元對象代 碼的最近的祖先相同。舉例來說就是,className()將不會返回你的類的實際名稱, 返回的是它的這個祖先的名稱。我們強烈建議QObject 的所有子類使用Q_OBJECT宏,而不管它們是否實際使用了信號、槽和屬性

http://blog.csdn.net/tingsking18/article/details/4800828

Qt元對象系統簡介