Qt核心的变更

Qt 6是通过有意识地提高框架的效率和易用性而产生的。

我们尝试在每个版本中保持所有公共API的二进制和源代码兼容性。但是在努力将Qt打造成更好的框架的过程中,一些变化是不可避免的。

在这个主题中,我们总结了Qt核心中的这些变更,并提供了处理它们的指导。

容器类

QHash,QMultiHash,QSet

qHash()签名

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

也就是说,你需要改为

uint qHash(MyType x, uint seed);

size_t qHash(MyType x, size_t seed);

这允许QHashQMultiHashQSet在64位平台上持有超过2^32个项目。

引用稳定性

Qt 6中QHashQMultiHashQSet的实现从基于节点的策略改为两阶段查找表。这种设计允许保持哈希实例内存开销非常小,同时保持良好的性能。

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

删除QHash::insertMulti

在Qt 5中,可以通过QHash::insertMulti使用QHash创建多值哈希,而QMultiHashQHash派生。

在Qt 6中,这两种类型和用途都是独立的,因此已删除QHash::insertMulti。

QVector,QList

在 Qt 6 之前,QVectorQList 是独立类。在 Qt 6 中,它们得到了统一:Qt 5 中的 QList 实现已被移除,两个类都使用更新的 QVector 实现。 QList 是具有实际实现的类,而 QVectorQList 的别名(typedef)。

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

API 变更

QList(以及 QVector)的大小类型从 int 更改为 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 个指针的大小。

同时,元素的内存布局也得到了更新。现在,QList 始终直接在其分配的内存区域中存储其元素,与 Qt 5 不同,当时某些对象分别在堆上分配,并将对象的指针放置到 QList 中。

请注意,后者特别影响大对象。为了获得 Qt 5 的行为,您可以先将对象包装到智能指针中,然后将这些智能指针直接存储到 QList 中。在这种情况下,您的 QList 的类型将是 QList<MySmartPointer<MyLargeObject>>,而不是在 Qt 5 中的 QList<MyLargeObject>

引用稳定性

QVector/QList 实现进行了一些更改。与 QVector 相关的是:开始插入得到优化(类似于 Qt 5 中的 QList)。与 QList 相关的是:元素的记忆布局得到简化。

重要: 这些更改影响引用的稳定性。在 Qt 6 中,您应该考虑任何修改大小或容量的方法会导致所有引用无效,即使 QList 不是隐式共享的。违反此规则的特例有明确的文档说明。

依赖于某些引用稳定性的应用程序在升级到使用 Qt 6 后可能会遇到未定义的行为。您应该特别注意使用非 C 兼容数组布局的 QVectorQList 的情况。

Qt6 中的视图类

概述

随着 Qt6 的推出,出现了几个新的 View 类。其中包括已有的 QStringView,现在还有 QByteArrayView,之后是专门的 QUtf8StringView 和更通用的 QAnyStringView

基于 QStringView 的视图类简介

QStringView 类提供了对 UTF-16 字符串的统一视图,这是基于 QString API 的只读子集。与保持自己字符串副本(可能是引用计数的)的 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." 的视图。

视图作为函数参数

视图应该按值传递,而不是按常量引用传递。

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'

视图的所有权模型

由于视图不拥有它们所引用的内存,必须仔细确保所有代码路径上引用的数据(例如,由 QString 拥有的)都比视图长存。

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
}

重要注意事项

利用新的视图类,可以在许多用例中实现很多性能提升。但是,重要的是要知道可能存在一些注意事项。因此,很重要要记住

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

QStringView 类

从 Qt6 开始,一般建议使用 QStringView 而不是 QStringRefQStringView 引用了 UTF-16 字符串的一个连续片段,它并不拥有这个片段。它作为所有 UTF-16 字符串的接口类型,无需首先构造一个 QStringQStringView 类公开了几乎所有的只读方法,包括之前存在的 QString 和旧的 QStringRef 类。

注意:必须注意确保所有代码路径上所引用的字符串数据(例如,由 QString 拥有)都超出了 QStringView 的生命周期。

注意:如果 QStringView 包裹了一个 QString,则需要小心,因为与 QStringRef 不同,一旦 QString 数据移动,QStringView 将不会更新内部数据指针。

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 6 中,QRecursiveMutex 已不再继承自 QMutex。此更改是为了提高 QMutexQRecursiveMutex 的性能。

由于这些更改,QMutex::RecursionMode 枚举已被弃用,而 QMutexLocker 现在是一个可以用于 QMutexQRecursiveMutex 的模板类。

QFuture 类

为了避免意外使用 QFuture,Qt 6 对 QFuture API 进行了一些更改,这可能会导致源代码兼容性问题。

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

QFuture<T>T 的转换已被禁用。铸造运算符调用的是 QFuture::result(),如果在尝试转换之前用户通过 QFuture::takeResult() 移除了存储在 QFuture 中的结果,可能会导致未定义的行为。需要显式使用 QFuture::result() 或 QFuture::takeResult() 方法来转换 QFuture<T>T

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

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

等价操作符

已删除 QFuture 的等价操作符。它们比较的是底层的数据指针,而不是比较结果,这不符合用户可能期望的行为。如果需要比较 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()。此更改不会影响使用 QFutureQtConcurrent 的代码的行为。只可能影响使用未记录的 QFutureInterface 的代码。
  • QFutureWatcher::isFinished() 现在反映的是 QFuture 的完成状态,而不是在 QFutureWatcher::finished() 已发送之前返回 false。

QPromise 类

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

I/O 类

QProcess 类

在 Qt 6 中,将单个命令字符串解析成程序名称和参数的重载函数 QProcess::start()() 已更名为 QProcess::startCommand()()。然而,还存在一个接受单个字符串以及 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 以 Win32 PROCESS_INFORMATION 结构访问 Q_PID 中数据的代码不再受支持。

元类型系统

QVariant 类

QVariant 已重写,以使用 QMetaType 完成所有操作。这导致一些方法的行为发生变化

  • QVariant::isNull() 现在仅在 QVariant 为空或包含 nullptr 时返回 true。在 Qt 5 中,如果某个类在 qtbase 中具有自己的 isNull 方法并返回 true,它也将返回 true。依赖于旧行为的代码需要检查包含的值是否返回 isNull - 然而,此类代码在实际情况中很少发生,因为 isNull() 很少是感兴趣的属性(比较 QString::isEmpty() / isNull()QTime::isValid / isNull)。
  • 在 Qt 6 中,QVariant::operator== 使用 QMetaType::equals。因此,某些图形类型,如 QPixmapQImageQIcon 将永远不会相等。此外,存储在 QVariant 中的浮点数不再与 qFuzzyCompare 进行比较,而是使用精确比较。

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

QMetaType 类

在 Qt 6 中,比较器注册和 QDebug 以及 QDataStream 流操作符的注册是自动完成的。因此,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转义符一起使用,无论指定的数字位数多少。
  • c{.}QRegExp中默认匹配所有字符,包括换行符。默认情况下,QRegularExpression排除了换行符。要包括换行符,请设置QRegularExpression::DotMatchesEverythingOption模式选项。
  • QRegExp默认进行Unicode感知匹配,而QRegularExpression需要单独的选项;下文将提供更多详细信息。
  • QRegExp中的c{.}默认匹配所有字符,包括换行符。默认情况下,QRegularExpression排除了换行符。要包括换行符,设置QRegularExpression::DotMatchesEverythingOption模式选项。

关于QRegularExpression支持的正则表达式语法的概述,请参阅QRegularExpressionpcrepattern(3)手册页,其中描述了PCRE(Perl兼容正则表达式的参考实现)支持的图案语法。

从QRegExp::exactMatch()迁移

QRegExp::exactMatch()有两个用途:它将正则表达式与主字符串精确匹配,并实现了部分匹配。

从QRegExp的精确匹配迁移

精确匹配指示正则表达式是否匹配整个主字符串。例如,在 subjects 字符串中的类会产生 "abc123"

QRegExp::exactMatch()QRegularExpressionMatch::hasMatch()
"\\d+"falsetrue
"[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匹配任何具有Unicode Nd(十进制数字)属性的字符。

在这些字符类中,默认情况下使用QRegularExpression仅匹配ASCII字符:例如,\d精确匹配ASCII范围内的字符0-9。可以通过使用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匹配选项来模拟QRegExp::CaretAtOffset的行为。其他QRegExp::CaretMode模式没有等效选项。

QRegExp类

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

QEvent及其子类

即使在是具有多态性的类中,QEvent类也定义了拷贝构造函数和赋值运算符。拷贝具有虚拟方法的类可能会导致在将不同类的对象相互赋值时发生裁剪。由于拷贝和赋值往往隐式发生,这可能会导致难以调试的问题。

在Qt 6中,QEvent子类的拷贝构造函数和赋值运算符已被设置为受保护的,以防止隐式拷贝。如果您需要拷贝事件,请使用方法,它将返回一个堆分配的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中,为了支持标准化的CBOR格式,移除了将QJsonDocument转换为其/从Qt的遗留JSON二进制格式的QJsonDocument方法。Qt JSON类型可以转换为Qt CBOR类型,这些类型可以进一步序列化为CBOR二进制格式,反之亦然。请参阅例如QCborValue::fromJsonValue()和QCborValue::toJsonValue()。

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

其他类

在 Qt 5 中,QCoreApplication::quit() 与调用 QCoreApplication::exit() 相当。这仅退出主事件循环。

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

调用 QCoreApplication::exit() 以保持非条件行为。

由于命名不一致,不再支持 QLibraryInfo::location() 和 QLibraryInfo::Location。请使用新的 API QLibraryInfo::path() 和 QLibraryInfo::LibraryPath

Qt 状态机框架

Qt 状态机 已移动到 Qt SCXML 模块(即将更名为 Qt 状态机),因此它不再是 Qt 核心的一部分。Qt 核心中存在很少的跨依赖项,这最终导致了这一决定。

使用 Qt5Compat 模块

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

QT += core5compat

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

PUBLIC_LIBRARIES
    Qt::Core5Compat

© 2024 The Qt Company Ltd. 本文件中的文档贡献权归其各自所有者所有。本文件提供的文档根据 Free Software Foundation 发布的 GNU 自由文档许可证版本 1.3 的条款提供。Qt 及相关标志是 The Qt Company Ltd. 在芬兰和/or 其他国家的商标。所有其他商标均为其各自所有者的财产。