Qt moc系統的黑魔法?

Qt的元對象系統(Meta-Object System)是Qt框架的核心功能之一,為C++語言增加了一些動態特性,借助元對象系統Qt可以實現以下功能

  • 信號與槽機制(Signals and Slots)
  • 運行時類型信息(Run-Time Type Information, RTTI)
  • 屬性系統(Property System)
  • 動態對象創建(Dynamic Object Creation)
  • 動態方法調用(Dynamic Method Invocation)
  • 安全的類型轉換(Dynamic Cast)

元對象系統的實現需要借助moc (Meta-Object_Compiler)工具, moc工具掃描包含Q_OBJECT宏的頭文件,為為我們自動生成了元對象系統的實現代碼,我們以一個簡單的MainWindow例子來探究一下moc系統有什么黑魔法,示例完整代碼放在文章最后。

QObject派生類的元信息編碼和存儲

元(Meta)在C++中是和編譯期相關的概念,元對象用來存儲一個類在編譯器就確定的類型相關信息, 即每一個類的類型和方法信息都是編譯期都是已經確定了的,在一般自己實現C++反射系統的方案中,存儲類的元信息,無不例外的都是借助一個靜態成員變量,由于靜態變量是存儲在靜態存儲區,生命周期伴隨整個程序,并且其初始化是在程序加載后,main入口函數之前,所以用一個靜態類來存儲元信息便很自然了,如下,Qt中就是給每一個定義Q_OBJECT宏的類定義了一個static QMetaObject成員對象。

staticMetaObject這個靜態成員的定義就在moc工具自動生成的moc_mainwindow.cpp文件之中,其中主要包含類的派生信息和信號與槽函數信息。

qt_meta_stringdata_MainWindow和qt_meta_data_MainWindow的定義在下面兩張截圖之中,我們先來看一下qt_meta_stringdata_MainWindow,這個結構里面將信號和槽函數的信息編碼存儲到了一個字符串之中,QT_MOC_LITERAL(0, 0, 10)中,第一個參數為編號,第二個為在下面字符串中的起始位置,第二個參數為函數名稱的長度。

qt_meta_data_MainWindow這個結構指定了每個信號和槽函數在上面的“字符串”中的位置以及函數的返回值,參數類型信息,這兩個結構就編碼存儲了信號槽函數的所有信息了。

槽函數的調用

這里先討論一個問題,如果知道一個函數的字符串名稱,怎么調用這個函數? 注意,這里的問題是,你現在只知道一個字符串, 雖然你知道它是那個函數,但是你怎么來調用?

std::string func_name = "my_slot";
//how to call my_slot() ?
func_name()?  // this is error~//the possiable way
if(func_name == "my_slot"){my_slot();
} else if( func_name = "my_slot1"){my_slot1();
}

在老版本的Qt中,connect鏈接信號和槽的時候,也是直接連接的函數字符串名稱,通過字符串來調用函數,就不得不借用我們moc文件里面生成的qt_static_metacall了,這里的實現也是很明了,首先找到函數的索引,然后通過switch case進行函數調用。

void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::InvokeMetaMethod) {auto *_t = static_cast<MainWindow *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->my_signal(); break;case 1: _t->my_signal_param((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< double(*)>(_a[2]))); break;case 2: _t->on_actionExit_triggered(); break;case 3: _t->my_slot(); break;case 4: _t->my_slot_param((*reinterpret_cast< int(*)>(_a[1]))); break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);{using _t = void (MainWindow::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::my_signal)) {*result = 0;return;}}{using _t = void (MainWindow::*)(int , double );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::my_signal_param)) {*result = 1;return;}}}
}

信號函數的實現

我們在定義信號的時候,只給出了一個信號函數的申明,信號函數的實現也是moc自動生成的

// SIGNAL 0
void MainWindow::my_signal()
{QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}// SIGNAL 1
void MainWindow::my_signal_param(int _t1, double _t2)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t2))) };QMetaObject::activate(this, &staticMetaObject, 1, _a);
}

總結:

我們可以發現,QT的元對象系統并沒有什么神奇之處,主要干了下面幾件事情

  • 通過一個靜態成員QMetaObject存儲類的元信息
  • 通過一個字符串編碼存儲了信號和槽函數信息
  • 通過信號和槽函數的索引,在switch case中調用槽函數
  • 自動生成信號函數的實現代碼

mainwindow示例:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = 0);explicit MainWindow(QWidget* parent, int id);~MainWindow();void my_general_fun();private slots:void on_actionExit_triggered();void my_slot();void my_slot_param(int iiii);signals:void my_signal();void my_signal_param(int jjjj, double kkkk);private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

moc生成的實現代碼:

/****************************************************************************
** Meta object code from reading C++ file 'mainwindow.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.15.2)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/#include <memory>
#include "../../../../mainwindow.h"
#include <QtCore/qbytearray.h>
#include <QtCore/qmetatype.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'mainwindow.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.15.2. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endifQT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_MainWindow_t {
QByteArrayData data[10];
char stringdata0[99];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_MainWindow_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_MainWindow_t qt_meta_stringdata_MainWindow = {
{
QT_MOC_LITERAL(0, 0, 10), // "MainWindow"
QT_MOC_LITERAL(1, 11, 9), // "my_signal"
QT_MOC_LITERAL(2, 21, 0), // ""
QT_MOC_LITERAL(3, 22, 15), // "my_signal_param"
QT_MOC_LITERAL(4, 38, 4), // "jjjj"
QT_MOC_LITERAL(5, 43, 4), // "kkkk"
QT_MOC_LITERAL(6, 48, 23), // "on_actionExit_triggered"
QT_MOC_LITERAL(7, 72, 7), // "my_slot"
QT_MOC_LITERAL(8, 80, 13), // "my_slot_param"
QT_MOC_LITERAL(9, 94, 4) // "iiii"},
"MainWindow\0my_signal\0\0my_signal_param\0"
"jjjj\0kkkk\0on_actionExit_triggered\0"
"my_slot\0my_slot_param\0iiii"
};
#undef QT_MOC_LITERALstatic const uint qt_meta_data_MainWindow[] = {
// content:
8,       // revision
0,       // classname
0,    0, // classinfo
5,   14, // methods
0,    0, // properties
0,    0, // enums/sets
0,    0, // constructors
0,       // flags
2,       // signalCount// signals: name, argc, parameters, tag, flags
1,    0,   39,    2, 0x06 /* Public */,
3,    2,   40,    2, 0x06 /* Public */,// slots: name, argc, parameters, tag, flags
6,    0,   45,    2, 0x08 /* Private */,
7,    0,   46,    2, 0x08 /* Private */,
8,    1,   47,    2, 0x08 /* Private */,// signals: parameters
QMetaType::Void,
QMetaType::Void, QMetaType::Int, QMetaType::Double,    4,    5,// slots: parameters
QMetaType::Void,
QMetaType::Void,
QMetaType::Void, QMetaType::Int,    9,0        // eod
};void MainWindow::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{if (_c == QMetaObject::InvokeMetaMethod) {auto *_t = static_cast<MainWindow *>(_o);Q_UNUSED(_t)switch (_id) {case 0: _t->my_signal(); break;case 1: _t->my_signal_param((*reinterpret_cast< int(*)>(_a[1])),(*reinterpret_cast< double(*)>(_a[2]))); break;case 2: _t->on_actionExit_triggered(); break;case 3: _t->my_slot(); break;case 4: _t->my_slot_param((*reinterpret_cast< int(*)>(_a[1]))); break;default: ;}} else if (_c == QMetaObject::IndexOfMethod) {int *result = reinterpret_cast<int *>(_a[0]);{using _t = void (MainWindow::*)();if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::my_signal)) {*result = 0;return;}}{using _t = void (MainWindow::*)(int , double );if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MainWindow::my_signal_param)) {*result = 1;return;}}}
}QT_INIT_METAOBJECT const QMetaObject MainWindow::staticMetaObject = { {QMetaObject::SuperData::link<QMainWindow::staticMetaObject>(),qt_meta_stringdata_MainWindow.data,qt_meta_data_MainWindow,qt_static_metacall,nullptr,nullptr
} };const QMetaObject *MainWindow::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}void *MainWindow::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_meta_stringdata_MainWindow.stringdata0))return static_cast<void*>(this);return QMainWindow::qt_metacast(_clname);
}int MainWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{_id = QMainWindow::qt_metacall(_c, _id, _a);if (_id < 0)return _id;if (_c == QMetaObject::InvokeMetaMethod) {if (_id < 5)qt_static_metacall(this, _c, _id, _a);_id -= 5;} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 5)*reinterpret_cast<int*>(_a[0]) = -1;_id -= 5;}return _id;
}// SIGNAL 0
void MainWindow::my_signal()
{QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}// SIGNAL 1
void MainWindow::my_signal_param(int _t1, double _t2)
{void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))), const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t2))) };QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

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

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

相關文章

【高頻】從輸入URL到頁面展示到底發生了什么?

一、相關衍生面試問題&#xff1a; 瀏覽器輸入美團網站&#xff0c;從回車到瀏覽器展示經歷了哪些過程 &#xff1f; http輸入網頁之后的流程&#xff1f; 百度搜索頁面&#xff0c;從點開搜索框&#xff0c;到顯示搜索頁面經歷了什么&#xff1f; 二、探究各個過程&#x…

XML Schema 字符串數據類型及約束詳解

字符串數據類型用于包含字符字符串的值。字符串數據類型可以包含字符、換行符、回車符和制表符。 以下是模式中字符串聲明的示例&#xff1a; <xs:element name"customer" type"xs:string"/>您文檔中的一個元素可能如下所示&#xff1a; <custo…

maven--解決Idea沒有拉取SNAPSHOT的問題

原文網址&#xff1a;maven--解決Idea沒有拉取SNAPSHOT的問題_IT利刃出鞘的博客-CSDN博客 簡介 本文介紹Idea解決maven沒有拉取SNAPSHOT的問題。 問題描述 項目依賴了以-SNAPSHOT結尾的包&#xff0c;它更新了。Idea點了重新導入后沒有拉取到最新的包&#xff1a; 解決方案…

什么是分賬系統呢?云分帳系統的實現功能有哪些?

隨著電商平臺的發展&#xff0c;越來越多的商家開始通過在線平臺進行銷售&#xff0c;平臺業務場景的逐漸復雜&#xff0c;多渠道收款、多方分賬。在些場景下如何解決這些問題成為電商平臺急需解決的核心問題。 為了解決這些問題&#xff0c;電商平臺可以引入分賬系統即可輕松解…

從“圖形可視化”到“圖生代碼”,低代碼平臺的新挑戰

前言&#xff1a; 低代碼平臺最大的一個特點就是可視化&#xff0c;將代碼采用可視化的方式展示管理。一時間擁有圖形化界面的各類系統都掛上了低代碼的標簽。但更多的代碼從業者在使用中卻發現&#xff0c;在眾多的低代碼平臺中都是“別人家的代碼”其可視化主要是別人家的代…

如何解決vcruntime140.dll丟失問題,詳細介紹5種靠譜的解決方法

vcruntime140.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;它為使用Visual C編譯器開發的應用程序提供必要的運行時環境。該DLL文件包含了大量應用程序運行時需要調用的庫函數&#xff0c;這些函數是實現C標準庫、異常處理機制、RTTI&#xff08;運行…

圖搜索算法教程(個人總結版)

圖搜索算法是一類用于遍歷或搜索圖結構的算法&#xff0c;廣泛應用于網絡分析、路徑規劃、人工智能等領域。常見的圖搜索算法包括深度優先搜索&#xff08;DFS&#xff09;、廣度優先搜索&#xff08;BFS&#xff09;、Dijkstra算法、A*算法等。本文將詳細介紹這些圖搜索算法的…

創建JSON數據包

在C語言中&#xff0c;JSON不是一種內置的數據類型&#xff0c;因此你需要使用第三方庫來創建和解析JSON數據。一個流行的庫是cJSON&#xff0c;它允許你以C語言的方式操作JSON數據。 以下是一個使用cJSON庫創建類似于你給出的JSON數據包的示例&#xff1a; 首先&#xff0c;…

go-zero 實戰(5)

引入Prometheus 用 Prometheus 監控應用 1. 用 docker 啟動 Prometheus 編輯配置位置&#xff0c;我將 prometheus.yaml 和 targets.json 文件放在了 /opt/prometheus/conf目錄下 prometheus.yaml global:scrape_interval: 15s # 抓取間隔evaluation_interval: 15s # 評估…

【代碼隨想錄 二叉樹】二叉樹前序、中序、后序遍歷的迭代遍歷

文章目錄 1. 二叉樹前序遍歷&#xff08;迭代法&#xff09;2. 二叉樹后序遍歷&#xff08;迭代法&#xff09;3. 二叉樹中序遍歷&#xff08;迭代法&#xff09; 1. 二叉樹前序遍歷&#xff08;迭代法&#xff09; 題目連接 &#x1f34e;因為處理順序和訪問順序是一致的。所…

前端工程化-babel、corejs、postcss

出處&#xff1a;前端工程化-babel、corejs、postcss | 劉維_個人博客_編程秘籍_開發技巧_入門到精通_生活感悟 (ldlw.site) 一. babel和corejs的作用到底是什么 腦子里面的想法 es6 -> es5 es6里面其實有兩種東西 語法 新特性 轉的語法 const a 1 const b &#xf…

Shader GLSL 3D旋轉函數

mat4 rotationMatrix(vec3 axis, float angle) {axis = normalize(axis);float s = sin(angle);float c = cos(angle)

類和對象的基本概念

類和對象的基本概念 C和C中struct區別類的封裝封裝訪問權限總結struct和class的區別 將成員變量設置為private C和C中struct區別 C語言struct只有變量C語言struct 既有變量&#xff0c;也有函數 類的封裝 封裝 把變量&#xff08;屬性&#xff09;和函數&#xff08;操作&a…

交換機部分綜合實驗

實驗要求 1.內網IP地址使用172.16.0.0/16 2.sw1和sW2之間互為備份; 3.VRRP/mstp/vlan/eth-trunk均使用; 4.所有pc均通過DHcP獲取Ip地址; 5.ISP只配置IP地址; 6.所有電腦可以正常訪問IsP路由器環回 實驗拓撲 實驗思路 1.給交換機創建vlan&#xff0c;并將接口劃入vlan 2.在SW1和…

Unity Render Streaming 云渲染 外網訪問

初版&#xff1a; 日期&#xff1a;2024.5.20 前言&#xff1a;臨時思路整理&#xff0c;后期會詳細補充 環境&#xff1a; 1. 阿里云服務器 需要安裝好nodejs 、npm 2. windows電腦&#xff0c;需安裝好 nodejs 、npm 3.Unity 2021.3.15f1 4.Unity Render Streaming …

31.GDB介紹及簡單使用

文章目錄 基本用法查看匯編代碼Text User Interface(TUI)refernece 歡迎訪問個人網絡日志&#x1f339;&#x1f339;知行空間&#x1f339;&#x1f339; GDB 是 GNU Debugger的縮寫&#xff0c;是GNU軟件系統中的標準調試器&#xff0c; 很多類UNIX系統都可以使用GDB&#xf…

【論文解讀】Overview of the Scalable Video Coding Extension of the H.264/AVC Standard

介紹 該篇論文是一篇關于H.264/AVC標準可擴展視頻編碼(SVC)擴展的綜述論文,由Heiko Schwarz、Detlev Marpe和Thomas Wiegand撰寫,發表在《IEEE Transactions on Circuits and Systems for Video Technology》2007年9月第17卷第9期上。 論文解讀 摘要: H.264/AVC視頻編…

鄉村振興的農業供給側結構性改革:優化農業產業結構,提升農產品質量,滿足市場需求,實現美麗鄉村產業振興

一、引言 鄉村振興戰略是我國當前及未來一段時間內的重大戰略部署&#xff0c;旨在推動農業農村現代化&#xff0c;實現城鄉融合發展。在鄉村振興戰略中&#xff0c;農業供給側結構性改革是核心任務之一。通過優化農業產業結構、提升農產品質量、滿足市場需求&#xff0c;不僅…

韓國云主機遠程故障怎么排查?

韓國云主機遠程故障可能是由于多種原因引起的&#xff0c;包括網絡問題、服務器故障、安全設置、客戶端問題等。下面是針對韓國云主機遠程故障的排查步驟和解決方法&#xff1a; 檢查網絡連接 1.使用 ping 命令 在本地計算機上使用 ping 命令檢查與云主機之間的網絡連接。如果無…

AI巨頭爭相與Reddit合作:為何一個古老的論壇成為AI訓練的“寶藏”?

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…