深入研究Qt Meta - Object System

目錄

先說RTTI

再說QMeta Object System

關于Q_OBJECT


這篇文章我打算研究一下QMetaObject System,也就是Qt自己構建起來的元對象系統。

先說RTTI

啥是RTTI?這是C++編程里的一個常見術語,全稱是:運行階段類型識別(Runtime Type Identification),關于RTTI如何在原生C++中使用不是我們這里的重點,但是可以明確的一點是——跟編譯器實現密切相關,意味著可移植性略差。很多類庫已經為其類對象提供了實現這種功能的方式,但由于C++內部并不支持,因此各個廠商的機制通常互不兼容

即使編譯器支持RTTI,就目前而言,原生的支持仍然十分的不足。我們沒有辦法完全知道例如類的名字、有哪些父類、有哪些成員變量、有哪些成員函數、哪些是public的、哪些是private的、哪些是protected的等等。

有時候一個工程項目可能包含成千上萬個類,完整的保存這些信息將會消耗大量的內存資源。為了節省內存,C++標準約定typeid只能返回類名。因此,僅靠dynamic_cast和typeid兩個關鍵字提供的類型信息實在有限。更何況,他還會造成大量的系統開銷,這也是為什么這個特性并沒有被完整的納入標準。

關于RTTI,可以參看:【C++】RTTI有什么用?怎么用? - 知乎 (zhihu.com)以備快速的復習

再說QMeta Object System

下面我們聊聊,既然大家都各做各的,Qt框架作為C++早期時代就存在的框架,自然實現了自己的一套源系統機制。

這個元對象機制不光實現了類似于RTTI那樣的動態查看類信息的作用,還擴展出了信號與槽的機制(這個就是大名鼎鼎的信號與槽)

Qt's meta-object system provides the signals and slots mechanism for inter-object communication, run-time type information, and the dynamic property system.

這個對象說一千道一萬,三個核心

  1. The QObject class provides a base class for objects that can take advantage of the meta-object system.

  2. The Q_OBJECT macro inside the private section of the class declaration is used to enable meta-object features, such as dynamic properties, signals, and slots.

  3. The Meta-Object Compiler (moc) supplies each QObject subclass with the necessary code to implement meta-object features.

也就是說:

  1. QObject這個類提供了整個元對象系統的一個根基

  2. Q_Object宏這是讓一個類可以使用RTTI,信號與槽機制(這就是為什么一些奇奇怪怪的Undefined Reference可以依賴這個解決,下一次發現使用信號與槽機制的時候編譯炸了排查的時候考慮這個事情)

  3. Moc則是更加進一步的提供了元對象系統的實現的保證(嘿!想一下你編譯的時候是不是需要有moc文件,他就是Meta-object Compilers,元系統編譯器產生的)

換而言之,Qt的元對象并不完全直接依賴于語言,而是借助了外來的Moc Tools預先掃描源文件,生成自己的元對象文件,在最后納入編譯階段合并進來

當然,我們的元對象系統還可以做更多的事情:

  1. QObject::metaObject作為一個靜態方法返回關聯的metaObject(也就是返回當前對象的元對象系統的那部分)

  2. QMetaObject::className可以進一步返回運行時的對象名稱,而這個是基于標準實現而不是編譯器實現的,你知道的,一致性!

  3. QObject::inherits則是檢查一個類是不是位于Qt的繼承樹上

  4. QObject::tr則是保證了我們的對象名稱滿足國際化

  5. QObject::setProperty和QObject::property讓我們的對象擁有了屬性這個概念!

  6. QMetaObject::newInstance()以一種工廠方法構造了這個類的一個新實例

我們知道dynamic_cast可以用來轉化父類子類,而且轉化成不成功全看是不是真的如此。這里我們入鄉隨俗,使用qobject_cast來檢查Qt元對象的繼承問題。

我隨手寫一個簡單的demo:

#include <QWidget>
#include <QMainWindow>
#include <QApplication>
class MyObject : public QWidget{};
?
?
int main(int argc, char *argv[])
{QApplication app(argc, argv); // Import For QWidgets enableQObject* obj = new MyObject;
?QWidget* widget = qobject_cast<QWidget*>(obj);if(widget){qDebug() << "Is Widget";}
?QMainWindow* window = qobject_cast<QMainWindow*>(obj);if(window){qDebug() << "Is Window";}
?delete obj;
}

值得注意的是,如果我們希望納入一個類進入QObject的繼承對象樹中,務!必!在私有區域聲明一個Q_OBJECT。(當然要是想要直接暴露給外面的話放在public也不是不行)

手擼了一個例子

#include <QWidget>
#include <QMainWindow>
#include <QApplication>
#define IS_USE_QOBJ_MACRO 0
?
class MyObject : public QWidget{
#if IS_USE_QOBJ_MACROQ_OBJECT
#endif
public:QString _ClassName(){return this->metaObject()->className();}
};
?
?
int main(int argc, char *argv[])
{QApplication app(argc, argv);QObject* obj = new MyObject;
?QWidget* widget = qobject_cast<QWidget*>(obj);if(widget){qDebug() << "Is Widget";}
?QMainWindow* window = qobject_cast<QMainWindow*>(obj);if(window){qDebug() << "Is Window";}
?qDebug() << dynamic_cast<MyObject*>(obj)->_ClassName();
?delete obj;
}
?
#if IS_USE_QOBJ_MACRO
#include "main.moc" // 一個Demo,我們直接自己引入編譯好的main.moc
#endif

你可以留意到,添加了QOBJECT宏的類的行為表現的并不一致。

#define IS_USE_QOBJ_MACRO 0
Is Widget
"QWidget"
#define IS_USE_QOBJ_MACRO 1
Is Widget
"MyObject"

由此,如果想要讓元對象系統正確的工作,請務必使用Q_OBJECT

關于Q_OBJECT

#define Q_OBJECT \
public: \QT_WARNING_PUSH \Q_OBJECT_NO_OVERRIDE_WARNING \static const QMetaObject staticMetaObject; \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \QT_TR_FUNCTIONS \
private: \Q_OBJECT_NO_ATTRIBUTES_WARNING \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \QT_WARNING_POP \struct QPrivateSignal { explicit QPrivateSignal() = default; }; \QT_ANNOTATE_CLASS(qt_qobject, "")

這就是我們的源碼。

可以看到他實際上就是向我們的類內嵌入了工作函數。這就是為什么需要添加一些類。

當然還有MOC編譯器的使用,以及還有屬性系統,挖個坑,有空講。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13994.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13994.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13994.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Chrome DevTools攻略

Chrome DevTools&#xff0c;也稱為Chrome開發者工具&#xff0c;是一套直接內置于Google Chrome瀏覽器的Web開發者工具。以下是一些使用Chrome DevTools的攻略和技巧&#xff1a; 打開DevTools&#xff1a; 右鍵點擊頁面上的任何元素&#xff0c;選擇“檢查”或“審查元素”。…

2024年華為OD機試真題-機場航班調度程序-C++-OD統一考試(C卷D卷)

題目描述: XX市機場停放了多架飛機,每架飛機都有自己的航班號CA3385,CZ6678,SC6508等,航班號的前2個大寫字母(或數字)代表航空公司的縮寫,后面4個數字代表航班信息。但是XX市機場只有一條起飛用跑道,調度人員需要安排目前停留在機場的航班有序起飛。為保障航班的有序起…

【webrtc】MediaEngine的實現CompositeMediaEngine創建VOE

m98音視頻的引擎是管理channel的看起來是外部強加給CompositeMediaEngine 管理的。CompositeMediaEngine :合成媒體引擎 G:\CDN\rtcCli\m98\src\media\base\media_engine.h// CompositeMediaEngine constructs a MediaEngine from separate // voice and video engine classes…

Python中文分詞工具庫之jieba使用詳解

概要 在自然語言處理(NLP)領域,中文文本的分詞是一個重要且基礎的任務。Python的jieba庫是一個廣泛使用的中文分詞工具,提供了豐富的功能,包括精準模式、全模式、搜索引擎模式等,適用于不同的應用場景。本文將詳細介紹jieba庫,包括其安裝方法、主要特性、基本和高級功能…

代碼隨想錄35期Day49-Java

Day49題目 LeetCode123買賣股票三 核心思想:和昨天的買賣股票相比,這個只允許買兩次,因此把狀態新增幾個,可見代碼注釋 class Solution {public int maxProfit(int[] prices) {// 設置五個狀態 0 : 無操作 , 1 : 第一次買入, 2 : 第一次賣出 , 3: 第二次買入, 4:第二次賣出…

java技術:oauth2協議

目錄 一、黑馬程序員Java進階教程快速入門Spring Security OAuth2.0認證授權詳解 1、oauth服務 WebSecurityConfig TokenConfig AuthorizationServer 改寫密碼校驗邏輯實現類 2、oauth2支持的四種方式&#xff1a; 3、oauth2授權 ResouceServerConfig TokenConfig 4、…

前端面試題日常練-day19 【面試題】

題目 希望這些選擇題能夠幫助您進行前端面試的準備&#xff0c;答案在文末。 1. AJAX是什么的縮寫&#xff1f; A. Asynchronous JavaScript and XMLB. Asynchronous JavaScript and XHTMLC. Asynchronous Java and XMLD. Asynchronous Java and XHTML2. 下列哪個方法用于創建…

SpringCloudAlibaba 動態讀取配置文件的信息

傳統讀取方式&#xff1a; 在application.properties中寫入要讀取的內容&#xff0c;如下&#xff1a; coupon.user.nameTom coupon.user.age27 接口引入處&#xff1a; Value("${coupon.user.name}")private String name;Value("${coupon.user.age}")p…

MySQL的索引是什么

MySQL的索引 一、索引概述二、索引結構1.簡要概述2.從二叉樹說起3.再在說下B-Tree4.為什么選擇BTree5.Hash又是什么6.博主被面試官經常問的題目 三、索引分類四、聚集索引&二級索引五、索引語法 一、索引概述 1.索引是幫助MySQL 高效獲取數據的數據結構(有序)。在數據之外…

[STM32-HAL庫]Flash庫-HAL庫-復雜數據讀寫-STM32CUBEMX開發-HAL庫開發系列-主控STM32F103C6T6

目錄 一、前言 二、實現步驟 1.STM32CUBEMX配置 2.導入Flash庫 3.分析地址范圍 4.找到可用的地址 5.寫入讀取普通數據 6.寫入讀取字符串 6.1 存儲相關信息 6.2 存取多個參數 三、總結及源碼 一、前言 在面對需要持久化存儲的數據時&#xff0c;除了掛載TF卡&#xff0c;我們…

燃數科技前端25-40K*14薪一面超簡單,下周二面啦

一面 1、自我介紹 2、低代碼如何設計的 3、react路由原理 4、react生命周期 5、什么是回調地獄&#xff0c;如何解決 6、jwt和session有什么區別 7、js文件相互引用有什么問題&#xff1f;如何解決 8、一個很大的json文件&#xff0c;前端讀取如何優化 面試我的不像是…

為什么說 Redis 是單線程的?——Java全棧知識(25)

為什么說 Redis 是單線程的&#xff1f; 我們常說的 Redis 是單線程的&#xff0c;但是我前面在講持久化機制的時候又說 RDB 的持久化是通過主進程 fork 出一個子進程來實現 RDB 持久化。那么 Redis 到底是多線程還是單線程的呢&#xff1f; Redis 的網絡 IO 和鍵值的讀寫是單…

力扣:1306. 跳躍游戲 III

1306. 跳躍游戲 III 這里有一個非負整數數組 arr&#xff0c;你最開始位于該數組的起始下標 start 處。當你位于下標 i 處時&#xff0c;你可以跳到 i arr[i] 或者 i - arr[i]。 請你判斷自己是否能夠跳到對應元素值為 0 的 任一 下標處。 注意&#xff0c;不管是什么情況下…

數據庫|基于T-SQL創建數據庫

哈嘍&#xff0c;你好啊&#xff0c;我是雷工&#xff01; SQL Server用于操作數據庫的編程語言為Transaction-SQL,簡稱T-SQL。 本節學習基于T-SQL創建數據庫。以下為學習筆記。 01 打開新建查詢 首先連接上數據庫&#xff0c;點擊【新建查詢】打開新建查詢窗口&#xff0c; …

appium-driver方法待整理。。

app C:\Users\v-hongweishi\AppData\Local\Programs\Xmind\Xmind.exe deviceName DESKTOP-7NJ1ENB platformName Windows 應用程序ID&#xff08;AppId&#xff09;是應用程序用戶模型 ID (AppUserModelID)&#xff0c;簡稱 AUMID Outlook …

Leetcode 113:路徑總和II

給你二叉樹的根節點 root 和一個整數目標和 targetSum &#xff0c;找出所有 從根節點到葉子節點 路徑總和等于給定目標和的路徑。 葉子節點 是指沒有子節點的節點。 public static List<List<Integer>> pathSum(TreeNode root, int targetSum) {List<List&l…

C++—結構體

結構體&#xff08;struct&#xff09;&#xff0c;是一種用戶自定義復合數據類型&#xff0c;可以包含不同類型的不同成員。 結構體的聲明定義和使用的基本語法&#xff1a; // 聲明結構體struct 結構體類型 { 成員1類型 成員1名稱; ...成員N類型 成員N名稱; };除聲明…

【計算機視覺(2)】

基于Python的OpenCV基礎入門——視頻的處理 視頻OpenCV視頻處理操作&#xff1a;創建視頻對象判斷視頻是否成功初始化讀取視頻幀獲取視頻特征設置視頻參數聲明編碼器保存視頻釋放視頻對象 視頻處理基本操作的代碼實現&#xff1a; 視頻 視頻是由一系列連續的圖像幀組成的。每一…

Spring—IoC

目錄 1. IoC的提出 2. Spring容器 2.1. Spring容器實現原理 2.2. Spring組件 2.2.1 XML標簽方式 2.2.2. 類注解方式 2.2.3. 方法注解方式 2.3. Spring容器分類 2.3.1. BeanFactory容器 2.3.2. ApplicationContext容器 2.3.3. WebApplicationContext容器 3. Spring中…

Srping 歷史

一、History of Spring and the Spring Framework Spring came into being in 2003 as a response to the complexity of the early J2EE specifications. While some consider Java EE and its modern-day successor Jakarta EE to be in competition with Spring, they are …