Qt元類型系統(QMetaType)詳解

Qt元類型系統詳解

  • 一、Qt元類型系統(QMetaType)詳解
    • 1. 核心功能
    • 2. 注冊機制
    • 3. 關鍵技術點
    • 4. 信號槽支持
    • 5. 流式傳輸支持
    • 6. 使用場景
    • 7. 注意事項
  • 二、完整示例
    • 1、基本實例
    • 2、基本實例
    • 3、元類型在信號槽中的應用
    • 4、高級用法
  • 三、元對象編譯器moc
    • 元對象編譯器(Moc)簡介
    • Moc的工作原理

一、Qt元類型系統(QMetaType)詳解

Qt元類型系統是Qt框架的核心機制之一,為運行時類型信息(RTTI)提供支持,使Qt的信號槽、屬性系統、QVariant等機制能夠處理自定義數據類型。以下是關鍵要點:

1. 核心功能

  • 類型注冊:為自定義類型分配唯一ID
  • 動態創建:運行時構造/析構對象
  • 類型轉換:支持QVariant與自定義類型互轉
  • 跨線程通信:確保類型安全的數據傳遞
  • 類型信息:提供類型名稱、大小等元信息

2. 注冊機制

聲明宏(頭文件中):

class CustomType { /*...*/ };
Q_DECLARE_METATYPE(CustomType)  // 聲明元類型

運行時注冊(cpp文件中):

qRegisterMetaType<CustomType>("CustomType");  // 注冊到元對象系統

要使自定義類型能夠用于Qt的元類型系統,需要滿足以下條件:

  • 必須是值類型(可拷貝)
  • 具有公共的默認構造函數
  • 具有公共的拷貝構造函數
  • 具有公共的析構函數

3. 關鍵技術點

  • 類型ID獲取
    int typeId = qMetaTypeId<CustomType>();  // 獲取唯一類型ID
    
  • 動態對象操作
    void* obj = QMetaType::create(typeId);    // 創建實例
    QMetaType::destroy(typeId, obj);          // 銷毀實例
    
  • QVariant集成
    CustomType data;
    QVariant var = QVariant::fromValue(data);  // 包裝為QVariant
    CustomType copy = var.value<CustomType>(); // 解包數據
    

4. 信號槽支持

注冊后可在跨線程信號槽中使用:

// 信號聲明
signals:void dataReady(const CustomType&);// 連接前注冊(確保線程安全)
qRegisterMetaType<CustomType>();
connect(sender, &Sender::dataReady, receiver, &Receiver::handleData);

5. 流式傳輸支持

如需支持QDataStream序列化:

// 注冊流操作符
qRegisterMetaTypeStreamOperators<CustomType>("CustomType");// 實現操作符重載
QDataStream& operator<<(QDataStream& out, const CustomType& obj);
QDataStream& operator>>(QDataStream& in, CustomType& obj);

6. 使用場景

  1. QVariant數據容器:存儲任意類型數據
    QVariantList list;
    list << QVariant::fromValue(CustomType());
    
  2. 動態屬性系統
    QObject obj;
    obj.setProperty("customProp", QVariant::fromValue(CustomType()));
    
  3. 跨線程通信:保證自定義類型在信號槽中的類型安全

7. 注意事項

類型注冊的必要性
自定義類型必須通過qRegisterMetaType()Q_DECLARE_METATYPE()注冊,否則無法用于信號槽跨線程通信或QVariant存儲。基本類型(如intQString)已由Qt內置注冊。

線程安全與信號槽
跨線程傳遞自定義類型時,必須確保類型已注冊且可構造/復制/銷毀。未注冊類型會導致運行時警告:“QObject::connect: Cannot queue arguments of type ‘MyClass’”。

QVariant的限制
使用QVariant::fromValue()QVariant::value<T>()時,類型必須滿足:

  • 默認構造函數
  • 拷貝構造函數
  • 公開的析構函數
  • 使用Q_DECLARE_METATYPE宏聲明

類型名稱沖突
避免不同類型使用相同名稱注冊,否則可能導致運行時行為異常。可通過QMetaType::type("MyClass")檢查是否已注冊。

動態多態類型處理
涉及繼承的類需額外處理:

// 基類注冊
Q_DECLARE_METATYPE(MyBaseClass*)
// 派生類注冊
qRegisterMetaType<MyDerivedClass*>("MyDerivedClass*");

移動語義支持
Qt 5及更高版本支持移動語義,但需確保類型實現移動構造函數和移動賦值運算符。對于資源管理類尤為重要。

模板類型處理
模板類需顯式實例化注冊:

typedef QMap<QString, MyClass> MyClassMap;
Q_DECLARE_METATYPE(MyClassMap)

二、完整示例

1、基本實例

// 自定義類型
struct Point3D {double x, y, z;Point3D(double a=0, double b=0, double c=0) : x(a), y(b), z(c) {}
};
Q_DECLARE_METATYPE(Point3D)// 主程序
int main() {qRegisterMetaType<Point3D>("Point3D");// QVariant使用Point3D p(1.0, 2.0, 3.0);QVariant var = QVariant::fromValue(p);Point3D p2 = var.value<Point3D>();// 動態創建int id = qMetaTypeId<Point3D>();void* mem = QMetaType::create(id);QMetaType::destroy(id, mem);return 0;
}

2、基本實例

Test.h

#ifndef TEST_H
#define TEST_H#include <QMetaType>
#include <QString>class Test {
public:Test (int numerator = 0, int denominator = 1): m_numerator(numerator), m_denominator(denominator) {}// 編譯器生成的默認拷貝構造函數和析構函數滿足要求QString toString() const {return QString("%1/%2").arg(m_numerator).arg(m_denominator);}double toDouble() const {returnstatic_cast<double>(m_numerator) / m_denominator;}int numerator() const { return m_numerator; }int denominator() const { return m_denominator; }private:int m_numerator;int m_denominator;
};Q_DECLARE_METATYPE(Test )  // 聲明Fraction為元類型#endif // Test 

main.cpp

#include <QCoreApplication>
#include <QVariant>
#include <QDebug>
#include "fraction.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);// 注冊元類型(用于信號槽連接)qRegisterMetaType<Test>();// 創建Fraction對象Fraction f1(3, 4);Fraction f2(1, 2);// 使用QVariant存儲自定義類型QVariant v1 = QVariant::fromValue(f1);QVariant v2;v2.setValue(f2);// 從QVariant中獲取值if (v1.canConvert<Fraction>()) {Fraction f = v1.value<Fraction>();qDebug() << "v1 contains:" << f.toString() << "=" << f.toDouble();}if (v2.canConvert<Fraction>()) {Fraction f = v2.value<Fraction>();qDebug() << "v2 contains:" << f.toString() << "=" << f.toDouble();}// 檢查類型信息qDebug() << "Type name:" << QMetaType::typeName(qMetaTypeId<Fraction>());qDebug() << "Type size:" << QMetaType::sizeOf(qMetaTypeId<Fraction>());// 動態創建Fraction實例void *ptr = QMetaType::create(qMetaTypeId<Fraction>());if (ptr) {Fraction *f = static_cast<Fraction*>(ptr);qDebug() << "Dynamically created:" << f->toString();QMetaType::destroy(qMetaTypeId<Fraction>(), ptr);}return a.exec();
}

3、元類型在信號槽中的應用

元類型系統使得自定義類型可以用于信號槽連接:

// 在頭文件中
signals:void fractionAdded(Fraction f);// 連接信號槽
QObject::connect(sender, &Sender::fractionAdded, receiver, &Receiver::handleFraction);// 需要在使用前調用
qRegisterMetaType<Fraction>("Fraction");

4、高級用法

流操作支持,要使自定義類型支持QDataStream的序列化,需要重載操作符:

QDataStream &operator<<(QDataStream &out, const Fraction &f) {out << f.numerator() << f.denominator();return out;
}QDataStream &operator>>(QDataStream &in, Fraction &f) {int num, den;in >> num >> den;f = Fraction(num, den);return in;
}

類型轉換,可以注冊自定義類型轉換函數:

QMetaType::registerConverter<Fraction, QString>(&Fraction::toString);

注冊后,可以直接將Fraction轉換為QString:

Fraction f(1, 2);
QString s = QVariant::fromValue(f).toString();

提到元類型就不得不提MOC編譯器了。

三、元對象編譯器moc

元對象編譯器(Moc)簡介

元對象編譯器(Meta-Object Compiler,簡稱Moc)是Qt框架的核心工具之一,用于處理Qt的信號與槽機制、運行時類型信息(RTTI)、屬性系統等元對象系統功能。Moc在編譯前對C++頭文件進行預處理,生成額外的元對象代碼,使Qt的元編程特性得以實現。

Moc的工作原理

Moc解析包含Q_OBJECT宏的C++頭文件,識別信號、槽、屬性等標記,并生成對應的元對象代碼(通常為moc_*.cpp文件)。生成的代碼會被編譯并鏈接到最終程序中,為Qt的動態特性(如信號與槽連接)提供運行時支持。

在這里插入圖片描述

源文件qconsole.h

#ifndef QONSOLE_H
#define QONSOLE_H#include <QMainWindow>
#include <QProcess>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>class Qonsole : public QMainWindow {Q_OBJECTpublic:Qonsole(QWidget *parent = nullptr) : QMainWindow(parent) {setupUI();setupProcess();}~Qonsole() {if (m_process->state() == QProcess::Running) {m_process->terminate();m_process->waitForFinished();}}private slots:void onReadyRead();void onReturnPressed();void onProcessStarted();void onProcessError(QProcess::ProcessError error);private:void setupUI();void setupProcess();void writeToConsole(const QString &text, const QColor &color = Qt::black);QProcess *m_process;QTextEdit *m_console;QLineEdit *m_commandInput;
};#endif // QONSOLE_H

編譯后的moc_qonsole.cpp文件

/****************************************************************************
** Meta object code from reading C++ file 'qonsole.h'
**
** Created by: The Qt Meta Object Compiler version 69 (Qt 6.9.0)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/#include "../../../qonsole.h"
#include <QtGui/qtextcursor.h>
#include <QtCore/qmetatype.h>#include <QtCore/qtmochelpers.h>#include <memory>#include <QtCore/qxptype_traits.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'qonsole.h' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 69
#error "This file was generated using the moc from 6.9.0. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif#ifndef Q_CONSTINIT
#define Q_CONSTINIT
#endifQT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
QT_WARNING_DISABLE_GCC("-Wuseless-cast")
namespace {
struct qt_meta_tag_ZN7QonsoleE_t {};
} // unnamed namespacetemplate <> constexpr inline auto Qonsole::qt_create_metaobjectdata<qt_meta_tag_ZN7QonsoleE_t>()
{namespace QMC = QtMocConstants;QtMocHelpers::StringRefStorage qt_stringData {"Qonsole","onReadyRead","","onReturnPressed","onProcessStarted","onProcessError","QProcess::ProcessError","error"};QtMocHelpers::UintData qt_methods {// Slot 'onReadyRead'QtMocHelpers::SlotData<void()>(1, 2, QMC::AccessPrivate, QMetaType::Void),// Slot 'onReturnPressed'QtMocHelpers::SlotData<void()>(3, 2, QMC::AccessPrivate, QMetaType::Void),// Slot 'onProcessStarted'QtMocHelpers::SlotData<void()>(4, 2, QMC::AccessPrivate, QMetaType::Void),// Slot 'onProcessError'QtMocHelpers::SlotData<void(QProcess::ProcessError)>(5, 2, QMC::AccessPrivate, QMetaType::Void, {{{ 0x80000000 | 6, 7 },}}),};QtMocHelpers::UintData qt_properties {};QtMocHelpers::UintData qt_enums {};return QtMocHelpers::metaObjectData<Qonsole, qt_meta_tag_ZN7QonsoleE_t>(QMC::MetaObjectFlag{}, qt_stringData,qt_methods, qt_properties, qt_enums);
}
Q_CONSTINIT const QMetaObject Qonsole::staticMetaObject = { {QMetaObject::SuperData::link<QMainWindow::staticMetaObject>(),qt_staticMetaObjectStaticContent<qt_meta_tag_ZN7QonsoleE_t>.stringdata,qt_staticMetaObjectStaticContent<qt_meta_tag_ZN7QonsoleE_t>.data,qt_static_metacall,nullptr,qt_staticMetaObjectRelocatingContent<qt_meta_tag_ZN7QonsoleE_t>.metaTypes,nullptr
} };void Qonsole::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{auto *_t = static_cast<Qonsole *>(_o);if (_c == QMetaObject::InvokeMetaMethod) {switch (_id) {case 0: _t->onReadyRead(); break;case 1: _t->onReturnPressed(); break;case 2: _t->onProcessStarted(); break;case 3: _t->onProcessError((*reinterpret_cast< std::add_pointer_t<QProcess::ProcessError>>(_a[1]))); break;default: ;}}
}const QMetaObject *Qonsole::metaObject() const
{return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}void *Qonsole::qt_metacast(const char *_clname)
{if (!_clname) return nullptr;if (!strcmp(_clname, qt_staticMetaObjectStaticContent<qt_meta_tag_ZN7QonsoleE_t>.strings))return static_cast<void*>(this);return QMainWindow::qt_metacast(_clname);
}int Qonsole::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 < 4)qt_static_metacall(this, _c, _id, _a);_id -= 4;}if (_c == QMetaObject::RegisterMethodArgumentMetaType) {if (_id < 4)*reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();_id -= 4;}return _id;
}
QT_WARNING_POP

在這里插入圖片描述

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

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

相關文章

《C++繼承詳解:從入門到理解公有、私有與保護繼承》

《C繼承詳解&#xff1a;從入門到理解公有、私有與保護繼承》 文章目錄《C繼承詳解&#xff1a;從入門到理解公有、私有與保護繼承》一、繼承的概念及定義1.1 繼承的概念1.2 繼承定義1.2.1 定義格式1.2.2 繼承基類成員訪問方式的變化1.3 繼承類模版二、基類和派生類間的轉換三、…

佳能iR-ADV C5560復印機如何掃描文件到電腦

打印機與電腦連接首先&#xff0c;確保佳能iR-ADV C5560復印機通過USB或Wi-Fi等網絡連接的方式成功連接到電腦。這可以通過USB線纜或Wi-Fi等網絡來實現。連接完成后&#xff0c;便可利用打印機內置的掃描功能&#xff0c;輕松將文件掃描并傳輸至電腦中。【掃描操作步驟】接下來…

騰訊AI IDE

1.官網說明&#xff1a;打開騰訊AI IDE官網。2.安裝說明&#xff1a;安裝成功后的界面。3.登錄 說明&#xff1a;通過郵箱和密碼登錄。4.成功說明&#xff1a;成功登錄如下界面。5.簡單一問說明&#xff1a;理解能力感覺不錯。擁有Claude-3.7-Sonnet??&#xff0c;??Claude…

【LeetCode 熱題 100】(一)哈希

1. 兩數之和 class Solution {public int[] twoSum(int[] nums, int target) {int length nums.length;// 1.聲明一個hashmap {nums[i], i}HashMap<Integer, Integer> map new HashMap<>();for (int i 0; i < length; i) {int second target - nums[i];if(m…

PMOS快速關斷電路、PMOS加速關斷電路

[電源系列]二、低成本MOS快速關斷電路原理分析 MOS的減速加速電路設計 分享一個微碧在網上看到的電路情況 加速電路1 PMOS關斷時間較長。 當用100kHz的頻率驅動PMOS時&#xff0c;PMOS G極的電壓信號并不是一個脈沖波&#xff0c;PMOS一直處于線性放大的狀態&#xff0c;并且…

Docker筆記(基本命令、掛載本地gpu、Dockerfile文件配置、數據掛載、docker換源)

Docker 主要用于環境搭建以及服務部署 基本命令 1.查看鏡像 docker images 2.查看容器 docker ps # 查看容器僅僅為查看運行狀態的容器 docker ps -a # 查看所有狀態的容器3.退出容器 exit4.刪除鏡像、容器 docker rm 鏡像ID docker rm 容器ID docker rm -f 容器ID # 強制刪除…

算法競賽階段二-數據結構(37)數據結構循環鏈表模擬實現

之前單鏈表中&#xff0c;數組全初始化為0&#xff0c;末尾最后一個next 存的就是0&#xff0c;指向的就是頭節點循環鏈表的基本概念循環鏈表是一種特殊的鏈表&#xff0c;其尾節點的指針域指向頭節點&#xff0c;形成一個閉環。與普通單鏈表相比&#xff0c;循環鏈表的遍歷需要…

20250727讓飛凌OK3576-C開發板在Rockchip的原廠Android14下通過耳機播音

20250727讓飛凌OK3576-C開發板在Rockchip的原廠Android14下通過耳機播音 2025/7/27 23:28緣起&#xff1a;很容易知道 飛凌OK3576-C開發板 使用的聲卡芯片是 NAU88C22YG 新唐科技(NUVOTON) NAU8822LYG NAU88C22YG 新唐立體聲音頻編解碼芯片原理圖&#xff1a;OK3576-C V1.2_202…

正向代理和反向代理的理解

**正向代理&#xff08;Forward Proxy&#xff09;和反向代理&#xff08;Reverse Proxy&#xff09;**是兩種不同類型的代理服務器&#xff0c;它們在數據傳輸過程中扮演的角色、使用場景以及工作方式都有所不同。 正向代理&#xff08;Forward Proxy&#xff09; 定義與作用&…

Java 后端 Cookie Session Token會話跟蹤技術

概述 會話從字面理解就是"兩方交流"&#xff0c;那問題就來了&#xff0c;HTTP&#xff08;超文本傳輸協議&#xff09;里面的"傳輸"不就包含了"兩方交流"的意思嗎&#xff1f;為什么要多此一舉提出會話技術呢&#xff1f; 談到這個&#xff0c;…

智譜AI GLM大模型 GLM-4-Plus的快速使用 ChatOpenAI類來調用GLM-4模型

智譜AIGLM-4&#xff0c;2024年1月16日發布的第四代基座大模型&#xff0c;其整體性能相較前代提升近60%&#xff0c;多維度指標逼近OpenAI的GPT-4水平。該模型支持128K上下文窗口&#xff08;約300頁文本處理能力&#xff09;&#xff0c;在長文本信息處理中實現100%精度召回。…

AsyncLocal淺復制的問題解決方案

針對C#中AsyncLocal<T>淺復制問題&#xff0c;以下是幾種主要的解決方案&#xff1a; 1. 使用不可變對象&#xff08;推薦&#xff09; 將存儲在AsyncLocal<T>中的對象設計為不可變的&#xff0c;避免修改共享狀態&#xff1a; public class ImmutableUserContext …

IIS發布.NET9 API 常見報錯匯總

記錄工作過程中發現的IIS常見錯誤。 1. HTTP Error 500.19 - Internal Server Error .NET 9 API --》vs打包方式如下&#xff1a; 發布到IIS后報錯HTTP Error 500.19 - Internal Server Error。 解決方案&#xff1a; 下載ASP.NET Core Hosting Bundle&#xff08;ASP.NET Co…

Google Chrome V8< 13.7.120 沙箱繞過漏洞

【嚴重】Google Chrome V8< 13.7.120 沙箱繞過漏洞 漏洞描述 V8 是 Google 開發的一款開源高性能 JavaScript 和 WebAssembly 引擎&#xff0c;廣泛用于 Chrome 瀏覽器和 Node.js 等項目中。 受影響版本中&#xff0c;JsonParser::MakeString 函數在處理長度為 1 的轉義字…

基于Spring Boot和Vue電腦維修平臺整合系統的設計與實現

用戶&#xff1a;注冊&#xff0c;登錄&#xff0c;在線報修&#xff0c;維修接單&#xff0c;維修報告&#xff0c;維修評價&#xff0c;個人資料維修工&#xff1a;登錄&#xff0c;在線報修&#xff0c;維修接單&#xff0c;維修報告&#xff0c;維修評價&#xff0c;通知公…

InsightFace(RetinaFace + ArcFace)人臉識別項目(預訓練模型,魯棒性很好)

背景介紹 這是一個 簡單的人臉識別項目&#xff0c;用 FastApi 在本地實現&#xff0c;使用預訓練模型&#xff0c;直接可用。 新方案比之前的FaceNet強太多了&#xff0c;甚至不用數據增強等操作&#xff0c;就可以識別戴眼鏡、不戴眼鏡、歪著的人臉等。 充分證明了選型的重要…

App Inventor 2 使用 MaterialIcons 圖標字體,快捷展示專業圖標

平時布局的話&#xff0c;如果要使用圖標&#xff0c;一般需要去找 png 圖片&#xff0c;且透明背景的。如果需要根據不同常見圖標進行變色的話&#xff0c;就需要準備多張不同樣式的圖標&#xff0c;還要考慮圖片的分辨率等等因素&#xff0c;非常的麻煩。 這時&#xff0c;如…

C語言——關于指針(逐漸清晰版)

為了更好地理解本篇文章的知識內容&#xff0c;讀者可以將以下文章作為補充知識進行閱讀 &#xff1a; C語言————原碼 補碼 反碼 &#xff08;超絕詳細解釋&#xff09;-CSDN博客 C語言————二、八、十、十六進制的相互轉換-CSDN博客 C語言————斐波那契數列的理解…

SVG 在線編輯器

SVG 在線編輯器 引言 隨著互聯網技術的發展&#xff0c;矢量圖形在網頁設計和數據可視化中扮演著越來越重要的角色。SVG&#xff08;可縮放矢量圖形&#xff09;因其文件小、無限縮放不模糊的特性&#xff0c;成為了網頁設計中常用的圖形格式。SVG 在線編輯器的出現&#xff0c…

libpostproc 已經從 ffmpeg 中移除,導致編譯 ffmpeg 時沒有 libpostproc

今天編譯 ffmpeg 時突然發現 libpostproc 不見了&#xff0c;-enable-postproc 也變成了非法的選項。用搜索引擎搜索相關信息找不到一點&#xff0c;于是去 github 看。 從提交記錄可以看到 libpostproc 已經被移除了 鏈接 主線中已經看不到了 libpostproc 這個目錄了