现代Qt开发教程(新手篇)1.5——变体与类型系统

张开发
2026/4/16 8:24:39 15 分钟阅读

分享文章

现代Qt开发教程(新手篇)1.5——变体与类型系统
现代Qt开发教程新手篇1.5——变体与类型系统相关仓库仍然已经开源正在积极火热的建设之中欢迎各位大佬提Issue和PR链接地址https://github.com/Awesome-Embedded-Learning-Studio/Tutorial_AwesomeQtPS: 嵌入式Linux的部分笔者还在研究驱动如何讲是合适的可能imx-forge的相关内容不得不托更几天确保质量准确这里特别给所有关心相关内容的朋友说明一下前言为什么需要万能类型说实话第一次见到 QVariant 的时候我心里是拒绝的。C 是强类型语言搞个什么都能装的容器这不是开历史倒车吗但等到你要写一个能同时存储 int、double、string 甚至自定义类型的配置系统时你会发现 QVariant 真香。QVariant 是 Qt 的万能容器它可以存储绝大多数 Qt 支持的类型。这在很多场景下非常有用比如解析 JSON 时你根本不知道下一个值是什么类型、比如做一个属性编辑器要支持各种数据类型、比如写一个通用的消息传递系统。这些问题用传统 C 方案要么写一堆 overload要么上模板把代码搞得像迷宫而 QVariant 提供了一个相对优雅的解决方案。不过这个万能是有代价的。类型安全检查只能到运行时用错了要等到程序跑起来才会炸。所以我们用 QVariant 的时候要格外小心该检查类型的时候千万别偷懒。环境说明本文代码基于 Qt 6.5 版本QVariant 在 Qt 6 中有一些行为变化主要是隐式转换被禁用这一点后面会专门说。如果你还在用 Qt 5某些写法可能需要调整。核心概念讲解QVariant 是什么从底层看 QVariant 就是一个带了类型标签的值容器。它内部维护一个类型 ID 和一块内存内存里存着实际的数据。你可以往里面塞 int塞 QString塞自定义类型只要你告诉 QVariant 这是什么类型它就能正确地存和取。先看个最简单的例子// 存储不同类型的值QVariant v142;// 存储 intQVariant v23.14;// 存储 doubleQVariant v3QString(Hello Qt);// 存储 QStringQVariant v4QColor(255,0,0);// 存储 QColor// 取出值注意要指定类型intiv1.toInt();// 42doubledv2.toDouble();// 3.14QString sv3.toString();// Hello QtQColor cv4.valueQColor();// QColor(255, 0, 0)你会发现存值很简单直接赋值就行。取值的时候要调用对应的转换函数或者用valueT()模板方法。这里有个关键点如果类型不匹配会怎样比如 v1 里存的是 int你调用toDouble()会怎样答案是 QVariant 会尝试做类型转换int 可以转成 double没问题。但如果 v3 存的是字符串你调用toInt()会怎样如果字符串内容不是数字转换失败会返回 0。这就是 QVariant 的坑点之一类型转换失败时你得不到明确的错误只能得到一个默认值。所以当你不确定类型时一定要先检查。常用类型的存储与提取Qt 已经为大多数内置类型注册了元类型可以直接用 QVariant 存储。这里列出一些最常用的类型存储方式提取方式int, double, float 等直接赋值toInt(), toDouble(), toFloat()QString直接赋值toString()bool直接赋值toBool()QList, QMap直接赋值toList(), toMap()QColor, QFont, QSize 等直接赋值value()自定义类型用 QVariant::fromValue()value()下面是一个稍微完整点的例子展示几种典型用法// 基本类型QVariant v142;QVariant v2true;QVariant v33.14;qDebug()v1.toInt()v2.toBool()v3.toDouble();// 字符串QVariant v4QString(Hello);qDebug()v4.toString();// 列表和映射QListQVariantlist{1,two,3.0};QVariant v5QVariant::fromValue(list);QMapQString,QVariantmap;map[name]Qt;map[version]6.5;QVariant v6QVariant::fromValue(map);注意这里的QVariant::fromValue()对于像 QList、QMap 这样的模板类型直接赋值可能有问题用fromValue()更保险。Qt 6 里这个问题更明显因为它禁用了一些隐式转换。类型检查的重要性刚才说到类型转换失败会返回默认值这可能导致你得到错误的结果而不自知。所以每次取值前最好先检查一下类型QVariant v123;// 不安全的做法直接转换intnumv.toInt();// 可能得到 123也可能得到别的// 安全的做法先检查类型if(v.type()QVariant::Int){intnumv.toInt();// 使用 num}elseif(v.type()QVariant::String){QString sv.toString();boolok;intnums.toInt(ok);if(ok){// 使用 num}}// 或者用 typeIdQt 6 推荐if(v.typeId()QMetaType::Int){// ...}type()返回的是QVariant::Type枚举这个在 Qt 6 里已经被标记为废弃推荐用typeId()配合QMetaType。但为了兼容性很多代码还在用type()。还有一个canConvert()方法它可以告诉你 QVariant 能否转换成目标类型QVariant v123;if(v.canConvertint()){intnumv.toInt();// 转换成功}QVariant v2hello;if(v2.canConvertint()){intnumv2.toInt();// 返回 0但 canConvert 可能说可以}这里有个坑canConvertint()对字符串来说总是返回 true因为字符串理论上可以转成 int即使是 “hello”。所以canConvert只是一个粗略的检查真正关键还是要看转换后的值是否合理。QMetaType 类型注册如果你想用 QVariant 存储自定义类型需要先把这个类型注册到 Qt 的元对象系统中。注册很简单用Q_DECLARE_METATYPE()宏// 在头文件中声明自定义类型structPerson{QString name;intage;};// 注册类型放在头文件中类定义之后Q_DECLARE_METATYPE(Person)// 现在可以用 QVariant 存储 PersonPerson p{Alice,30};QVariant vQVariant::fromValue(p);// 取出时也要用 valueT()Person p2v.valuePerson();注意注册要放在头文件中因为所有用到这个类型的地方都需要看到这个声明。如果你的类型在 cpp 文件中定义那就在 cpp 文件中注册。踩坑预防清单⚠️ 坑 #1忘记检查类型就转换❌ 错误做法QVariant vgetUserInput();// 不知道是什么类型intnumv.toInt();// 直接转换// 使用 num... ✅ 正确做法 cpp QVariant v getUserInput(); if (v.type() QVariant::Int || v.type() QVariant::Double) { int num v.toInt(); // 使用 num... } else { // 处理类型不匹配的情况 } 后果如果 v 里存的是字符串 “hello”toInt() 会返回 0你可能以为用户输入的是 0实际是错误的。 一句话记住取出 QVariant 的值前先问自己一句这真的对吗然后检查类型。⚠️ 坑 #2Qt 6 的隐式转换问题❌ 错误做法// Qt 5 可以这样写Qt 6 可能编译不过QListQVariantlist;list1two3.0; ✅ 正确做法 cpp // Qt 6 推荐写法 QListQVariant list {1, two, 3.0}; // 或者显式转换 QListQVariant list; list.append(QVariant(1)); list.append(QVariant(two)); list.append(QVariant(3.0)); 后果Qt 6 禁用了某些隐式转换直接用可能编译失败。 一句话记住Qt 6 里类型转换要显式点别指望编译器猜。⚠️ 坑 #3自定义类型没注册❌ 错误做法structPoint{intx,y;};// 忘记 Q_DECLARE_METATYPE(Point)Point p{1,2};QVariant vQVariant::fromValue(p);// 返回无效的 QVariant ✅ 正确做法 cpp struct Point { int x, y; }; Q_DECLARE_METATYPE(Point) // 记得注册 Point p{1, 2}; QVariant v QVariant::fromValue(p); // 正常工作 Point p2 v.valuePoint(); 后果没注册的类型存进 QVariant 会得到一个无效的 QVariant取出来也是垃圾值。 一句话记住自定义类型想进 QVariant先去 Q_DECLARE_METATYPE 报个到。随堂测验 口述回答用自己的话说说QVariant 和 C 的 std::any 有什么区别为什么 Qt 要搞自己的万能类型而不是用标准库的 代码填空// 下面代码想把一个 QVariant 里的值转换成字符串输出// 但有时候转换会失败请补全错误处理QVariant vgetSomeValue();QString str;if(v.type()QVariant::String){strv.______(1)______;}elseif(v.canConvertQString()){strv.______(2)______;}else{str____((unknown type));}qDebug()Value:str; 调试挑战这段代码想从用户输入读取一个数字但有一个 bug。问题在哪里QVariant inputQLineEdit::text();// 假设这是用户输入intnumberinput.toInt();// 转换成数字if(number100){qDebug()数字太大;}练习项目 练习项目简易配置编辑器 功能描述实现一个配置文件编辑器可以读取、修改、保存配置项。每个配置项都有一个名称和一个 QVariant 值支持 int、double、bool、string 等类型。✅ 完成标准定义一个Config类内部用QMapQString, QVariant存储配置项实现set(),get(),save(),load()方法get()方法要支持默认值如果配置项不存在返回默认值save()把配置保存到 JSON 文件load()从 JSON 文件加载配置写一个 main 函数演示如何使用这个 Config 类 提示用 QJsonDocument 处理 JSON 读写从 JSON 读值时得到的就是 QVariant直接存就行保存时注意处理不同类型QJsonObject 支持大多数 QVariant 类型官方文档参考链接 Qt 文档 · QVariant · QVariant 完整 API 参考必查文档 Qt 文档 · QMetaType · 了解 Qt 的类型系统和元类型注册机制 Qt 文档 · Container Classes · Qt 容器类与 QVariant 配合使用的说明相关阅读入门 · 环境搭建 · 00 · Qt6 安装踩坑指南 - 相似度 100%现代Qt开发——0.1——如何在IDE中配置Qt环境 - 相似度 100%现代Qt开发教程新手篇1.3——字符串与编码 - 相似度 100%

更多文章