Qt Core

Qt 6 是我们努力使框架更高效、更易用的结果。

我们尝试在每个版本中保持所有公共 API 的二进制和源代码兼容性。但为了使 Qt 成为更好的框架,有些改动是不可避免的。

在本专题中,我们总结了Qt Core 中的这些变化,并提供了处理这些变化的指导。

容器类

QHash, QMultiHash, QSet

qHash() 签名

对于自定义类型,QHashQMultiHash 依赖于您在同一命名空间中提供的custom qHash() function 。在 Qt 4 和 Qt 5 中,qHash 函数的返回值和可选的第二个参数的类型是uint 。在 Qt 6 中,它是size_t

也就是说,您需要将

uint qHash(MyType x, uint seed);

改为

size_t qHash(MyType x, size_t seed);

这样,在 64 位平台上,QHashQMultiHashQSet 可容纳超过 2^32 个项目。

引用的稳定性

Qt XML 6 中QHashQMultiHashQSet 的实现从基于节点的方法改为两阶段查找表。这种设计可以将散列实例的内存开销保持在很小的范围内,同时提供良好的性能。

需要注意的一个行为变化是,当表需要增长或条目被删除时,新的实现将不会为散列中的元素提供稳定的引用。依赖这种稳定性的应用程序现在可能会遇到未定义的行为。

删除 QHash::insertMulti

在 Qt 5 中,QHash 可通过使用 QHash::insertMulti 来创建多值散列,而QMultiHash 则派生了 vomQHash

在 Qt 6 中,这两种类型和用例截然不同,QHash::insertMulti 被删除。

QVector、QList

在 Qt 6 之前,QVectorQList 是独立的类。在 Qt 6 中,它们是统一的:QList QVector QList 是实际实现的类,而 是 的别名(类型定义)。QVector QList

QList在 Qt 6 中,fromVector() 和 toVector() 以及QVector 的 fromList() 和 toList() 不再涉及数据复制。现在,它们会返回被调用的对象。

API 变化

QList QVectorint 改为qsizetype 。连同大小类型,所有相关方法的签名都更新为使用qsizetype 。这样,QList 就可以在 64 位平台上容纳超过 2^31 个项目。

在将代码库升级到 Qt 6 时,这一 API 变化很可能会导致编译器发出关于缩小类型转换的警告。有了下面的示例代码:

void myFunction(QList<MyType> &data) {
    int size = data.size();
    // ...
    const int pos = getInsertPosition(size);
    data.insert(pos, MyType());
    // ...
}

您需要将其更新为使用qsizetype 或 auto 关键字:

void myFunction(QList<MyType> &data) {
    auto size = data.size();
    // ...
    const auto pos = getInsertPosition(size);
    data.insert(pos, MyType());
    // ...
}

或者,您也可以使用类型转换,将所有内容转换为intqsizetype

注意: 如果您想同时针对 Qt 5 和 Qt 6 构建代码,auto 关键字是一个很好的解决方案,它能覆盖不同版本之间的签名差异。

内存布局

QList 在 Qt 6 中,内存布局发生了多处变化。

在 Qt 5 中,sizeof(QList<T>) 等于指针的大小。现在,额外的指针间接被移除,QList 数据成员直接存储在对象中。默认情况下,希望sizeof(QList<T>) 等于 3 个指针的大小。

同时,还更新了元素的内存布局。与 Qt XML 5 不同的是,QList 现在总是将其元素直接存储在已分配的内存区域中,而在 Qt XML 5 中,某些对象是在堆上单独分配的,指向这些对象的指针则被放置到QList 中。

请注意,后者尤其会影响大型对象。要想拥有 Qt 5 行为,您可以将对象封装为智能指针,并将这些智能指针直接存储到QList 中。在这种情况下,您的QList 的类型将是QList<MySmartPointer<MyLargeObject>> ,而不是 Qt XML 5 中的QList<MyLargeObject>

引用的稳定性

QVector/QList 的实现有几处改动。与QVector 相关的是:优化了开头的插入(类似于 Qt 5 中的QList )。与QList 相关的是:简化了元素的内存布局。

重要: 这些更改会影响引用的稳定性。在 Qt 6 中,您应该考虑任何修改大小或容量的方法都会使所有引用失效,即使QList 没有隐式共享。此规则的例外情况已明确记录在案。

当升级到使用 Qt 6 时,依赖于某些引用稳定性的应用程序可能会遇到未定义的行为。如果QVectorQList 最初使用了非 C 兼容的数组布局,则应格外注意。

Qt6 中的视图类

概览

Qt6 中新增了几个View 类。其中包括已经存在的QStringView ,现在又增加了QByteArrayView ,以及专门的QUtf8StringView 和更通用的QAnyStringView

以 QStringView 为例介绍视图类

QStringView 类通过QString API 的只读子集为 UTF-16 字符串提供了统一的视图。QString 会保存自己的字符串副本(可能会进行反射计算),而QStringView 则不同,它提供的是存储在其他地方的字符串视图。

char hello[]{ "Hello." };   // narrow multi-byte string literal
QString str{hello};         // needs to make a copy of the string literal
QString strToStr(str);      // atomic increment involved to not create a copy of hello again

// The above code can be re-written to avoid copying and atomic increment.

QStringView view{ u"Hello." };  // view to UTF-16 encoded string literal
QStringView viewToView{ view }; // view of the same UTF-16 encoded string literal

字符串"Hello." 存储在二进制文件中,运行时不分配。view 只是字符串"Hello." 的视图,因此无需创建副本。当我们复制一个QStringView 时,viewToView 观察的字符串与从view 复制的字符串观察的字符串相同。这意味着viewToView 不需要创建副本或原子增量。它们是现有字符串"Hello." 上的视图。

视图作为函数参数

视图应通过值传递,而不是通过引用-const 传递。

void myfun1(QStringView sv);        // preferred
void myfun2(const QStringView &sv); // compiles and works, but slower

视图操作函数

QStringView 视图函数支持让我们操作字符串视图的函数。这样,我们就可以更改视图,而无需创建视图字符串的部分副本。

QString pineapple = "Pineapple";
QString pine = pineapple.left(4);

// The above code can be re-written to avoid creating a partial copy.

QStringView pineappleView{ pineapple };
QStringView pineView = pineappleView.left(4);

非空端字符串和包含'\0'

QStringView 支持空端字符串和非空端字符串。区别在于初始化 的方式:QStringView

QChar aToE[]{ 'a', 'b', 'c', 'd', 'e' };

QStringView nonNull{ aToE, std::size(aToE) }; // with length given
QStringView nonNull{ aToE }; // automatically determines the length

QChar fToJ[]{ 'f', 'g', 'h', '\0', 'j' };

// uses given length, doesn't search for '\0', so '\0' at position 3
// is considered to be a part of the string similarly to 'h' and 'j
QStringView nonNull{ fToJ, std::size(fToJ) };
QStringView part{ fToJ }; //stops on the first encounter of '\0'

视图的所有权模型

由于views 并不拥有其引用的内存,因此必须注意确保引用的数据(例如,由QString 所拥有)在所有代码路径上的寿命都超过view

QStringView sayHello()
{
    QString hello("Hello.");
    return QStringView{ hello }; // hello gets out of scope and destroyed
}

void main()
{
    QStringView hello{ sayHello() };
    qDebug() << hello; // undefined behavior
}

将 QStringView 转换为 QString

QStringView 不会隐式或显式地转换为 ,但可以创建其数据的深度副本:QString

void print(const QString &s) { qDebug() << s; }

void main()
{
    QStringView string{ u"string"};

    // print(string); // invalid, no implicit conversion
    // QString str{ string }; // invalid, no explicit conversion

    print(string.toString());
    QString str = string.toString(); // create QString from view
}

重要说明

通过利用新的视图类,我们可以在许多用例中大大提高性能。不过,需要注意的是,可能会有一些注意事项。因此,请务必记住

  • 视图应按值传递,而不是按引用-const 传递。
  • 以负长度构造视图是未定义的行为。
  • 必须注意确保所引用的数据(例如,由QString 拥有的数据)在所有代码路径上都比视图长。

QStringView 类

从 Qt6 开始,通常建议使用QStringView 而不是QStringRefQStringView 引用它不拥有的 UTF-16 字符串的连续部分。它是所有类型 UTF-16 字符串的接口类型,无需首先构建QStringQStringView 类公开了QString 和以前存在的QStringRef 类的几乎所有只读方法。

注: 必须注意确保引用的字符串数据(例如,由QString 所拥有)在所有代码路径上都比QStringView 长。

注意: 如果QStringView 封装了QString ,则需要注意,因为与QStringRef 不同,QStringView 不会在QString 数据重新定位后更新内部数据指针。

QString string = ...;
QStringView view{string};

// Appending something very long might cause a relocation and will
// ultimately result in a garbled QStringView.
string += ...;

QStringRef 类

在 Qt6 中,QStringRef 已从Qt Core 中移除。为了便于移植现有应用程序而不触及整个代码库,QStringRef 类并没有完全消失,而是被移到了 Qt5Compat 模块中。如果您想进一步使用QStringRef ,请参阅使用 Qt5Compat 模块

遗憾的是,由QString 公开的一些返回QStringRef 的方法无法移到 Qt5Compat 中。因此,可能需要进行一些手动移植。如果您的代码使用了以下一个或多个函数,您需要将它们移植到QStringViewQStringTokenizer 。对于性能关键的代码,建议使用QStringView::tokenize 而不是QStringView::split

更改使用QStringRef 的代码:

QString string = ...;
QStringRef left = string.leftRef(n);
QStringRef mid = string.midRef(n);
QStringRef right = string.rightRef(n);

QString value = ...;
const QVector<QStringRef> refs = string.splitRef(' ');
if (refs.contains(value))
    return true;

到:

QString string = ...;
QStringView left = QStringView{string}.left(n);
QStringView mid = QStringView{string}.mid(n);
QStringView right = QStringView{string}.right(n);

QString value = ...;
const QList<QStringView> refs = QStringView{string}.split(u' ');
if (refs.contains(QStringView{value}))
    return true;
// or
const auto refs = QStringView{string}.tokenize(u' ');
for (auto ref : refs) {
    if (ref == value)
        return true;
}

在 Qt XML 6 中,QRecursiveMutex 不再继承自QMutex 。这一更改是为了提高QMutexQRecursiveMutex 的性能。

由于这些更改,QMutex::RecursionMode 枚举已被移除,而QMutexLocker 现在是一个模板类,可同时在QMutexQRecursiveMutex 上运行。

QFuture 类

为了避免对QFuture 的意外使用,Qt 6 中对QFuture API 进行了一些修改,这些修改可能会带来源代码兼容性中断。

QFuture 与其他类型之间的隐式转换

已禁用QFuture<T>T 的转换。转换操作符调用了QFuture::result() ,如果用户在尝试进行转换之前通过QFuture::takeResult() 从QFuture 移动了结果,这可能会导致未定义的行为。在需要将QFuture<T> 转换为T 时,请明确使用QFuture::result() 或QFuture::takeResult() 方法。

QFuture<T>QFuture<void> 的隐式转换也已禁用。如果确实需要进行转换,请使用显式QFuture<void>(const QFuture<T> &) 构造函数:

QFuture<int> future = ...
QFuture<void> voidFuture = QFuture<void>(future);

等式运算符

QFuture 的相等运算符已被删除。它们比较的是底层 d-pointers 而不是比较结果,这与用户的期望不符。如果需要比较QFuture 对象,请使用QFuture::result()QFuture::takeResult() 方法。例如

QFuture<int> future1 = ...;
QFuture<int> future2 = ...;
if (future1.result() == future2.result())
    // ...

QFuture 和 QFutureWatcher 的行为变化

在 Qt 6 中,对QFutureQFutureWatcher 进行了一些改进,从而导致以下行为变化:

  • 暂停QFutureQFutureWatcher (通过调用pause()setPaused(true) )后,QFutureWatcher 不会立即停止发送进度和结果就绪信号。暂停时,可能仍有计算正在进行,无法停止。暂停后,这些计算的信号可能仍会发送,而不是推迟到下次恢复后才报告。要获得暂停实际生效的通知,可以使用QFutureWatcher::suspended() 信号。此外,还新增了isSuspending()isSuspended() 方法,用于检查QFuture 是否正在暂停或已处于暂停状态。需要注意的是,出于一致性考虑,QFutureQFutureWatcher 的暂停相关 API 已被弃用,取而代之的是名称中包含 "suspend "的类似方法。
  • QFuture::waitForFinished()现在将等待QFuture 实际进入完成状态,而不是在未进入运行状态时立即退出。这样可以防止waitForFinished() 在调用时未来尚未启动而立即退出。这同样适用于QFutureWatcher::waitForFinished()。这一更改不会影响与QtConcurrent 一起使用QFuture 的代码的行为。只有与未注明的QFutureInterface 一起使用的代码才会受到影响。
  • QFutureWatcher::isFinished()现在反映了QFuture 的完成状态,而不是在QFutureWatcher::finished() 发出之前返回 false。

QPromise 类

在 Qt XML 6 中,应使用新的QPromise 类代替非官方的 QFutureInterface 作为QFuture 的 "setter "对应类。

IO 类

QProcess 类

在 Qt XML 6 中,通过将单个命令字符串拆分为程序名称和参数来解释该字符串的QProcess::start() 重载已更名为QProcess::startCommand()。不过,还有一个QProcess::start() 重载可以接收单个字符串和QStringList 作为参数。由于QStringList 参数默认为空列表,因此只传递字符串的现有代码仍可编译,但如果是包含参数的完整命令字符串,则无法执行程序。

Qt 5.15 为相应的重载引入了弃用警告,以便于发现和更新现有代码:

QProcess process;

// compiles with warnings in 5.15, compiles but fails with Qt 6
process.start("dir \"My Documents\"");

// works with both Qt 5 and Qt 6; also see QProcess::splitCommand()
process.start("dir", QStringList({"My Documents"});

// works with Qt 6
process.startCommand("dir \"My Documents\"");

已删除 QProcess::pid() 和 Q_PID 类型;请使用QProcess::processId() 来获取本地进程标识符。不再支持使用本地 Win32 API 以 Win32PROCESS_INFORMATION 结构访问 Q_PID 中数据的代码。

元类型系统

QVariant 类

QVariant 已被重写,其所有操作均使用 。这意味着一些方法的行为发生了变化:QMetaType

  • QVariant::isNull() 现在,只有当 为空或包含 时,才返回 。在 Qt XML 5 中,如果 qtbase 中的类本身有 方法,则该方法也会返回 true。依赖于旧行为的代码需要检查所包含的值是否返回 isNull,但这种代码在实践中不太可能出现,因为 很少是人们感兴趣的属性(比较 / 和 / )。QVariant nullptr true isNull isNull() QString::isEmpty() isNull() QTime::isValid isNull
  • QVariant::operator== 在 Qt XML 6 中使用 。因此,一些图形类型(如 、 和 )永远不会比较相等。此外,存储在 中的浮点数不再与 进行比较,而是使用精确比较。QMetaType::equals QPixmap QImage QIcon QVariant qFuzzyCompare

此外,还删除了 QVariant::operator<、QVariant::operator<=、QVariant::operator> 和 QVariant::operator>=,因为不同的变体并不总是可排序的。这也意味着QVariant 不能再用作QMap 中的键。

QMetaType 类

在 Qt XML 6 中,比较器以及QDebugQDataStream 流运算符的注册是自动完成的。因此,QMetaType::registerEqualsComparator()QMetaType::registerComparators()qRegisterMetaTypeStreamOperators()QMetaType::registerDebugStreamOperator() 已不复存在。在移植到 Qt 6 时,必须删除对这些方法的调用。

类型注册

Q_PROPERTY 中使用的类型的元类型存储在类的QMetaObject 中。这就要求当 moc 看到这些类型时,它们必须是完整的,这可能会导致在 Qt 5 中运行的代码出现编译错误。有三种方法可以解决这个问题:

  • 包含定义类型的头文件。
  • 使用Q_MOC_INCLUDE 宏代替包含。如果包含头文件会导致循环依赖,或会减慢编译速度,则使用此方法会有帮助。
  • 如果头文件存在于实现类的 cpp 文件中,也可以在其中包含 moc 生成的文件。

正则表达式类

QRegularExpression 类

在 Qt 6 中,QRegExp 类型已退役至 Qt5Compat 模块,所有使用该类型的 Qt API 已从其他模块中移除。使用该类型的客户端代码可以移植到QRegularExpression 来代替。由于QRegularExpression 已存在于 Qt 5 中,因此可在移植到 Qt 6 之前完成并测试。

Qt 5 中引入的QRegularExpression 类实现了与 Perl 兼容的正则表达式,在提供的 API、支持的模式语法和执行速度方面都比QRegExp 有了很大改进。最大的区别在于,QRegularExpression 只是保存一个正则表达式,在请求匹配时不会被修改。相反,它会返回一个QRegularExpressionMatch 对象,用于检查匹配结果并提取捕获的子串。这同样适用于全局匹配和QRegularExpressionMatchIterator

其他区别概述如下。

注意: QRegularExpression 并不支持与 Perl 兼容的正则表达式的所有功能。最明显的一点是不支持捕获组的重复名称,使用重复名称可能导致未定义的行为。这可能会在 Qt 的未来版本中有所改变。

不同的模式语法

将正则表达式从QRegExp 移植到QRegularExpression 可能需要更改模式本身。

在特定情况下,QRegExp 过于宽松,接受的模式在使用QRegularExpression 时根本无效。这种情况很容易发现,因为使用这些模式构建的QRegularExpression 对象是无效的(参见QRegularExpression::isValid())。

在其他情况下,从QRegExp 移植到QRegularExpression 的模式可能会悄悄改变语义。因此,有必要对所使用的模式进行审查。最明显的不兼容情况包括

  • 使用像\xHHHH 这样超过 2 位数的十六进制转义字符时需要使用大括号。像\x2022 这样的模式需要移植到\x{2022} ,否则它将匹配空格 (0x20) 后的字符串"22" 。一般来说,强烈建议始终使用带大括号的\x 转义符,无论指定的位数是多少。
  • {,n} 这样从 0 到 n 的量化需要移植到{0,n} 以保留语义。否则,像\d{,3} 这样的模式将匹配一个数字后的精确字符串"{,3}"
  • QRegExp 默认情况下,"...... "会进行 Unicode 感知匹配,而 则需要一个单独的选项;详情见下文。QRegularExpression
  • QRegExp 中的 c{.} 默认匹配所有字符,包括换行符。QRegularExpression 默认不匹配换行符。要包含换行符,请设置QRegularExpression::DotMatchesEverythingOption pattern 选项。

有关QRegularExpression 支持的正则表达式语法概览,请参阅pcrepattern(3)man 页面,其中介绍了 PCRE(兼容 Perl 的正则表达式的参考实现)支持的模式语法。

从 QRegExp::exactMatch() 移植到 QRegExp::exactMatch()

QRegExp::exactMatch()有两个用途:将正则表达式与主题字符串进行精确匹配,以及实现部分匹配。

从 QRegExp 的精确匹配移植而来

精确匹配表示正则表达式是否与整个主题字符串匹配。例如,主题字符串"abc123"

QRegExp::exactMatch()QRegularExpressionMatch::hasMatch()
"\\d+"true
"[a-z]+\\d+"truetrue

QRegularExpression 中不反映精确匹配。如果要确保主题字符串与正则表达式完全匹配,可以使用QRegularExpression::anchoredPattern() 函数对模式进行包装:

QString p("a .*|pattern");

// re matches exactly the pattern string p
QRegularExpression re(QRegularExpression::anchoredPattern(p));
从 QRegExp 的部分匹配移植过来

在使用QRegExp::exactMatch() 时,如果没有找到完全匹配的正则表达式,还可以通过调用QRegExp::matchedLength() 找出主题字符串中有多少部分与正则表达式匹配。如果返回的长度等于主题字符串的长度,那么就可以断定找到了部分匹配。

QRegularExpression 通过适当的 ,正则表达式明确支持部分匹配。QRegularExpression::MatchType

全局匹配

由于QRegExp API 的限制,无法正确实现全局匹配(即像 Perl 那样)。特别是,可以匹配 0 个字符的模式(如"a*" )是有问题的。

QRegularExpression::globalMatch() 可以正确实现 Perl 的全局匹配,返回的迭代器可以用来检查每个结果。

例如,如果有类似于

QString subject("the quick fox");

int offset = 0;
QRegExp re("(\\w+)");
while ((offset = re.indexIn(subject, offset)) != -1) {
    offset += re.matchedLength();
    // ...
}

可以重写为

QString subject("the quick fox");

QRegularExpression re("(\\w+)");
QRegularExpressionMatchIterator i = re.globalMatch(subject);
while (i.hasNext()) {
    QRegularExpressionMatch match = i.next();
    // ...
}

支持 Unicode 属性

在使用QRegExp 时,字符类(如\w\d 等)会匹配具有相应 Unicode 属性的字符:例如,\d 会匹配任何具有 UnicodeNd (十进制数字)属性的字符。

使用QRegularExpression 时,这些字符类默认只匹配 ASCII 字符:例如,\d0-9 ASCII 范围内的字符完全匹配。使用QRegularExpression::UseUnicodePropertiesOption 模式选项可以改变这种行为。

通配符匹配

QRegularExpression 中没有直接进行通配符匹配的方法。不过,我们提供了QRegularExpression::wildcardToRegularExpression() 方法,用于将 glob 模式转换为与 Perl 兼容的正则表达式。

例如,如果您有如下代码

QRegExp wildcard("*.txt");
wildcard.setPatternSyntax(QRegExp::Wildcard);

可以将其重写为

auto wildcard = QRegularExpression(QRegularExpression::wildcardToRegularExpression("*.txt"));

但请注意,有些类似 shell 的通配符模式可能无法转换成您所期望的结果。如果使用上述函数进行简单转换,下面的示例代码就会自动中断:

const QString fp1("C:/Users/dummy/files/content.txt");
const QString fp2("/home/dummy/files/content.txt");

QRegExp re1("*/files/*");
re1.setPatternSyntax(QRegExp::Wildcard);
re1.exactMatch(fp1); // returns true
re1.exactMatch(fp2); // returns true

// but converted with QRegularExpression::wildcardToRegularExpression()

QRegularExpression re2(QRegularExpression::wildcardToRegularExpression("*/files/*"));
re2.match(fp1).hasMatch(); // returns false
re2.match(fp2).hasMatch(); // returns false

这是因为,默认情况下,QRegularExpression::wildcardToRegularExpression() 返回的正则表达式是完全锚定的。要获得非锚定的正则表达式,请在转换选项中输入QRegularExpression::UnanchoredWildcardConversion

QRegularExpression re3(QRegularExpression::wildcardToRegularExpression(
                           "*/files/*", QRegularExpression::UnanchoredWildcardConversion));
re3.match(fp1).hasMatch(); // returns true
re3.match(fp2).hasMatch(); // returns true

最小匹配

QRegExp::setMinimal() 通过简单地反转量词的贪婪程度来实现最小匹配(QRegExp 不支持懒惰量词,如*?,+? 等)。QRegularExpression 支持贪婪、懒惰和所有格量词。QRegularExpression::InvertedGreedinessOption 模式选项可以用来模拟QRegExp::setMinimal() 的效果:如果启用,它会反转量词的贪婪程度(贪婪的量词变成懒惰的,反之亦然)。

粗心模式

QRegularExpression::AnchorAtOffsetMatchOption match 选项可用于模拟QRegExp::CaretAtOffset 的行为。其他QRegExp::CaretMode 模式没有类似功能。

QRegExp 类

在 Qt6 中,QRegExp 已从Qt Core 中移除。如果您的应用程序现在无法移植,QRegExp 仍存在于 Qt5Compat 中,以保持这些代码库的正常工作。如果您想进一步使用QRegExp ,请参阅使用 Qt5Compat 模块

QEvent 和子类

QEvent 类定义了一个复制构造函数和一个赋值操作符,尽管它是一个多态类。在将不同类中的对象相互赋值时,复制具有虚拟方法的类可能会导致切分。由于复制和赋值通常是隐式的,这可能导致难以调试的问题。

在 Qt XML 6 中,QEvent 子类的复制构造函数和赋值运算符已受保护,以防止隐式复制。如果需要复制事件,请使用clone 方法,该方法将返回QEvent 对象的堆分配副本。请确保您删除了克隆对象,或许可以使用 std::unique_ptr,除非您发布了克隆对象(在这种情况下,Qt 会在发布后删除克隆对象)。

QEvent 子类中,覆盖 clone(),并像这样声明受保护和默认实现的复制构造函数和赋值操作符:

class MyEvent : public QEvent
{
public:
    // ...

    MyEvent *clone() const override { return new MyEvent(*this); }

protected:
    MyEvent(const MyEvent &other) = default;
    MyEvent &operator=(const MyEvent &other) = default;
    MyEvent(MyEvent &&) = delete;
    MyEvent &operator=(MyEvent &&) = delete;
    // member data
};

请注意,如果您的 MyEvent 类分配内存(例如通过指针到实现模式),那么您就必须实现自定义的复制语义。

序列化类

在 Qt 6 中,用于将其转换为/从 Qt 传统 JSON 二进制格式的QJsonDocument 方法被移除,转而使用标准化的 CBOR 格式。Qt JSON 类型可转换为 Qt CBOR 类型,而 Qt CBOR 类型又可序列化为 CBOR 二进制格式,反之亦然。例如,请参见QCborValue::fromJsonValue() 和QCborValue::toJsonValue()。

如果您仍然需要使用二进制 JSON 格式,可以使用 Qt5Compat 模块中提供的替换。您可以在QBinaryJson 命名空间中找到它们。请参阅使用 Qt5Compat 模块,了解如何在应用程序中使用该模块。

其他类

在 Qt XML 5 中,QCoreApplication::quit() 等同于调用QCoreApplication::exit() 。这只是退出了主事件循环。

在 Qt 6 中,该方法将通过发布关闭事件来尝试关闭所有顶级窗口。窗口可以通过忽略该事件来取消关闭进程。

调用QCoreApplication::exit() 可保持无条件行为。

由于命名不一致,QLibraryInfo::location() 和 QLibraryInfo::Location 已被弃用。请使用新的 APIQLibraryInfo::path() 和QLibraryInfo::LibraryPath

Qt State Machine 框架

Qt State Machine被移入Qt SCXML 模块(不久将更名为 Qt State Machine),因此它不再是Qt Core 的一部分。Qt Core 内部很少有交叉依赖,最终导致了这一决定。

使用 Qt5Compat 模块

要使用Qt5Compat模块,您需要在构建时将其头文件包含在包含路径中,并针对其库进行链接。如果使用qmake,请在.pro 文件中添加以下内容:

QT += core5compat

如果使用cmake 构建应用程序或库,请在CMakeList.txt 中添加以下内容:

PUBLIC_LIBRARIES
    Qt::Core5Compat

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.