Spec-Zone.ru › Qt

Изменения в Qt Core

Qt 6 — результат осознанных усилий по повышению эффективности и удобства использования фреймворка.

Мы стараемся поддерживать двоичную и исходную совместимость для всех публичных API в каждом релизе. Но некоторые изменения были неизбежны в стремлении сделать Qt лучшим фреймворком.

В этой теме мы обобщаем изменения в Qt Core и предоставляем рекомендации по их обработке.

Классы контейнеров

QHash, QMultiHash, QSet

Подпись qHash()

Для пользовательских типов QHash и QMultiHash требуют предоставления пользовательской функции 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);

Это позволяет QHash, QMultiHash и QSet хранить более 232 элементов на 64-разрядных платформах.

Стабильность ссылок

Реализация QHash, QMultiHash и QSet в Qt 6 была изменена с подхода, основанного на узлах, на двухэтапную таблицу поиска. Эта конструкция позволяет поддерживать очень низкую нагрузку на память экземпляра хеш-таблицы, обеспечивая при этом хорошую производительность.

Следует отметить одно изменение поведения: новая реализация не обеспечит стабильных ссылок на элементы в хеш-таблице при необходимости увеличения таблицы или удаления записей. Приложения, которые полагаются на такую стабильность, могут столкнуться с неопределенным поведением.

Удаление QHash::insertMulti

В Qt 5 QHash можно было использовать для создания хеш-таблиц с множественными значениями с помощью QHash::insertMulti, а QMultiHash наследовал от QHash.

В Qt 6 типы и случаи использования различны, и QHash::insertMulti был удален.

QVector, QList

До Qt 6 QVector и QList были отдельными классами. В Qt 6 они объединены: реализация Qt 5 QList убрана, и оба класса теперь используют обновлённую реализацию QVector. QList — класс с фактической реализацией, а QVector — псевдоним (typedef) для QList.

Методы fromVector() и toVector() для QList и fromList() и toList() для QVector в Qt 6 больше не подразумевают копирования данных. Теперь они возвращают объект, для которого они были вызваны.

Изменения API

Тип размера QList (и, следовательно, QVector) изменён с int на qsizetype. Вместе с типом размера обновлены подписи всех соответствующих методов для использования qsizetype. Это позволяет QList хранить более 231 элементов на 64-битных платформах.

При обновлении кодовой базы до 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());
    // ...
}

В качестве альтернативы можно использовать приведение типов и привести всё к int или к qsizetype.

Примечание: Если вы хотите создавать приложения, работающие как с 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>> вместо QList<MyLargeObject> в Qt 5.

Стабильность ссылок

В реализации QVector/QList внесены некоторые изменения. Изменения, связанные с QVector, заключаются в оптимизации вставки в начало (аналогично QList в Qt 5). Изменения, связанные с QList, заключаются в упрощении макета памяти для элементов.

Важно: Эти изменения влияют на стабильность ссылок. В Qt 6 любые методы, изменяющие размер или ёмкость, должны считаться вызывающими недействительность всех ссылок, даже когда QList не является неявным совмещением. Исключения из этого правила документированы явно.

Приложения, которые полагаются на определённую стабильность ссылок, могут столкнуться с неопределённым поведением при обновлении до Qt 6. Вам следует уделить особое внимание случаям, когда QVector или QList с макетом массива, не совместимого с C, использовались изначально.

Классы представлений в Qt6

Обзор

С Qt6 появились несколько новых View классов. Существующий QStringView теперь сопровождается QByteArrayView и дополняется специализированным QUtf8StringView и более универсальным QAnyStringView.

Введение в классы представлений на примере QStringView

Класс QStringView обеспечивает единое представление строковых значений UTF-16 с подмножеством методов QString только для чтения. В отличие от 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'

Модель владения представлениями

Так как 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
}

Важные замечания

Используя новые классы представлений, можно добиться значительного повышения производительности во многих случаях. Однако важно знать, что могут быть некоторые нюансы. Поэтому важно помнить:

  • Представления должны передаваться по значению, а не по ссылке-константе.
  • Создание представления с отрицательной длиной — неопределённое поведение.
  • Необходимо позаботиться о том, чтобы ссылаемые данные (например, принадлежащие QString) пережили представление на всех путях кода.

Классы, связанные со строками

Класс QStringView

Начиная с Qt6, рекомендуется использовать QStringView вместо QStringRef. QStringView ссылается на непрерывный участок строки UTF-16, которой она не владеет. Она действует как интерфейсный тип для всех типов строк UTF-16 без необходимости предварительного создания QString. Класс QStringView предоставляет почти все методы только для чтения 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 и добавить эту строку в ваш файл qmake .pro:

QT += core5compat

Если вы уже перенесли своё приложение или библиотеку в систему сборки cmake, добавьте следующее в ваш CMakeList.txt:

PUBLIC_LIBRARIES
    Qt::Core5Compat
END_OF_DOCUMENT_MARKER

К сожалению, некоторые методы, экспонированные классом QString и возвращающие QStringRef, не смогли быть перенесены в Qt5Compat. Поэтому может потребоваться ручная портировка. Если ваш код использует одну или несколько из следующих функций, вам необходимо перенести их на использование QStringView или QStringTokenizer. Также рекомендуется использовать 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;
}

QMutex и связанные классы

В Qt 6, QRecursiveMutex больше не наследуется от QMutex. Это изменение было сделано для повышения производительности как QMutex, так и QRecursiveMutex.

Из-за этих изменений перечисление QMutex::RecursionMode было удалено, а QMutexLocker теперь является шаблонным классом, который может работать как с QMutex, так и с QRecursiveMutex.

QFuture и связанные классы

Класс QFuture

Для предотвращения непреднамеренного использования QFuture, в API QFuture Qt 6 были внесены некоторые изменения, которые могут привести к проблемам совместимости исходного кода.

Неявные преобразования между QFuture и другими типами

Преобразование QFuture<T> в T было отключено. Оператор приведения типов вызывал QFuture::result(), что может привести к неопределенному поведению, если пользователь перемещает результаты из QFuture с помощью QFuture::takeResult() перед попыткой преобразования. Используйте методы 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 были удалены. Они сравнивали указатели d вместо сравнения результатов, что не соответствует ожиданиям пользователя. Если вам нужно сравнить объекты QFuture, используйте методы QFuture::result() или QFuture::takeResult(). Например:

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

Изменения в поведении QFuture и QFutureWatcher

В Qt 6 были внесены некоторые улучшения в QFuture и QFutureWatcher, что привело к следующим изменениям в поведении:

  • После приостановки QFuture или QFutureWatcher (вызовом pause() или setPaused(true)), QFutureWatcher не будет немедленно останавливать передачу сигналов о готовности прогресса и результата. В момент приостановки может продолжаться вычисление, которое не может быть остановлено. Сигналы для таких вычислений могут быть переданы после приостановки, а не отложены и сообщены только после возобновления. Чтобы получить уведомление о том, когда приостановление действительно вступило в силу, можно использовать сигнал QFutureWatcher::suspended(). Кроме того, есть новые методы isSuspending() и isSuspended(), чтобы проверить, находится ли QFuture в процессе приостановки или уже в приостановленном состоянии. Обратите внимание, что по соображениям согласованности API, связанные с приостановкой, для QFuture и QFutureWatcher были устаревшими и заменены аналогичными методами с «suspend» в названии.
  • QFuture::waitForFinished() теперь будет ожидать, пока QFuture фактически находится в завершенном состоянии, а не завершать работу как только оно не находится в состоянии выполнения. Это предотвращает waitForFinished() от немедленного выхода, если в момент вызова фьючерс еще не запущен. То же самое относится к QFutureWatcher::waitForFinished(). Это изменение не повлияет на поведение кода, который использовал QFuture с QtConcurrent. Только код, использующий его с недокументированным QFutureInterface может быть затронут.
  • QFutureWatcher::isFinished() теперь отражает состояние завершения QFuture, а не возвращает false до тех пор, пока не будет выпущен QFutureWatcher::finished().

Класс QPromise

В Qt 6 вместо неофициального QFutureInterface следует использовать новый класс QPromise в качестве «установщика» для QFuture.

Классы ввода-вывода

Класс QProcess

В Qt 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() для получения идентификатора процесса.

Система мета-типов

Класс QVariant

QVariant был переписан для использования QMetaType для всех своих операций. Это подразумевает изменения в поведении нескольких методов:

  • QVariant::isNull() теперь возвращает true только если QVariant пуста или содержит nullptr. В Qt 5 он также возвращал true для классов в qtbase, которые имели метод isNull если тот возвращал true. Код, полагающийся на старое поведение, должен проверить, возвращает ли содержащееся значение isNull – однако такой код маловероятен на практике, так как isNull() редко является интересующим свойством (сравните QString::isEmpty() / isNull() и QTime::isValid / isNull).
  • QVariant::operator== использует QMetaType::equals в Qt 6. Поэтому некоторые графические типы, такие как QPixmap, QImage и QIcon, никогда не будут сравниваться как равные. Кроме того, числа с плавающей точкой, хранящиеся в QVariant, больше не сравниваются с qFuzzyCompare, а вместо этого используются точные сравнения.

Кроме того, операторы QVariant::operator<, QVariant::operator<=, QVariant::operator> и QVariant::operator>= были удалены, так как разные варианты не всегда упорядочиваются. Это также означает, что QVariant больше не может использоваться в качестве ключа в QMap.

Класс QMetaType

В Qt 6 регистрация компараторов, операторов вывода cQDebug и QDataStream выполняется автоматически. Вследствие этого QMetaType::registerEqualsComparator(), QMetaType::registerComparators(), qRegisterMetaTypeStreamOperators() и QMetaType::registerDebugStreamOperator() больше не существуют. При портировании в Qt 6 вызовы этих методов должны быть удалены.

Регистрация типов

Типы, используемые в Q_PROPERTY, хранят свой мета-тип в QMetaObject класса. Это требует, чтобы типы были полными, когда moc видит их, что может привести к ошибкам компиляции в коде, который работал в Qt 5. Есть три способа исправить эту проблему:

  • Включить заголовок, который определяет тип.
  • Вместо использования включения, используйте макрос Q_MOC_INCLUDE. Это помогает, если включение заголовка привело бы к циклической зависимости или замедлило бы компиляцию.
  • Если заголовок присутствует в файле cpp, реализующем класс, можно также включить сгенерированный moc-файл.

Классы регулярных выражений

Класс QRegularExpression

В Qt6 из нашего кода были удалены все методы, принимающие QRegExp. Поэтому, очень вероятно, что вам придётся переносить ваше приложение или библиотеку на QRegularExpression.

QRegularExpression реализует Perl-совместимые регулярные выражения. Он полностью поддерживает Unicode. Для обзора синтаксиса регулярных выражений, поддерживаемого QRegularExpression, обратитесь к упомянутому руководству pcrepattern(3). Регулярное выражение состоит из двух частей: строки шаблона и набора параметров шаблона, которые изменяют значение строки шаблона.

Есть некоторые тонкие различия между QRegularExpression и QRegExp, которые будут объяснены в этом документе, чтобы облегчить процесс переноса.

QRegularExpression более строго относится к синтаксису регулярного выражения. Поэтому всегда полезно проверять выражение на действительность.

QRegularExpression почти всегда можно объявить const (кроме случаев, когда шаблон меняется), в то время как QRegExp почти никогда не мог быть таким.

Нет замены для перечисления CaretMode. Можно использовать опцию соответствия QRegularExpression::AnchoredMatchOption для эмуляции поведения QRegExp::CaretAtOffset. Нет эквивалента для других режимов QRegExp::CaretMode.

QRegularExpression поддерживает только Perl-совместимые регулярные выражения. Тем не менее, он не поддерживает все функции, доступные в Perl-совместимых регулярных выражениях. Наиболее заметным является то, что дублированные имена для групп захвата не поддерживаются, и их использование может привести к неопределенному поведению. Это может измениться в будущей версии Qt.

Сопоставление с шаблонами

Непосредственного способа выполнения подстановок по шаблонам в QRegularExpression нет. Однако, метод QRegularExpression::wildcardToRegularExpression предназначен для преобразования шаблонов glob в совместимый с Perl регулярный выражение, которое можно использовать для этой цели.

Например, если у вас есть код, подобный

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

его можно переписать как

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

Обратите внимание, что не все шаблоны подстановок, похожие на оболочечные, могут быть преобразованы ожидаемым образом. Следующий пример кода молча сломается, если просто преобразовать его с помощью упомянутой функции:

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

Поиск вперед

Поиск вперед внутри строки обычно реализовывался с помощью цикла, использующего QRegExp::indexIn и нарастающий смещение, но теперь его можно легко реализовать с помощью QRegularExpressionMatchIterator или QString::indexOf.

Например, если у вас есть код, подобный

QString subject("the quick fox");

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

его можно переписать как

QRegularExpression re("(\\w+)");
QString subject("the quick fox");

QRegularExpressionMatchIterator i = re.globalMatch(subject);
while (i.hasNext()) {
    QRegularExpressionMatch match = i.next();
    // ...
}

 // or alternatively using QString::indexOf

qsizetype from = 0;
QRegularExpressionMatch match;
while ((from = subject.indexOf(re, from, &match)) != -1) {
    from += match.capturedLength();
    // ...
}

Поиск назад

Поиск назад внутри строки обычно часто реализовывался как цикл по QRegExp::lastIndexIn, но теперь его можно легко реализовать, используя QString::lastIndexOf и QRegularExpressionMatch.

Примечание: QRegularExpressionMatchIterator не способен выполнять поиск назад.

Например, если у вас есть код, подобный

int offset = -1;
QString subject("Lorem ipsum dolor sit amet, consetetur sadipscing.");

QRegExp re("\\s+([ids]\\w+)");
while ((offset = re.lastIndexIn(subject, offset)) != -1) {
    --offset;
    // ...
}

его можно переписать как

qsizetype from = -1;
QString subject("Lorem ipsum dolor sit amet, consetetur sadipscing.");

QRegularExpressionMatch match;
QRegularExpression re("\\s+([ids]\\w+)");
while ((from = subject.lastIndexOf(re, from, &match)) != -1) {
    --from;
    // ...
}

exactMatch vs. match.hasMatch

QRegExp::exactMatch выполняло две функции: точное соответствие регулярного выражения строке-объекту и реализацию частичного соответствия. Точное соответствие указывает, соответствует ли регулярное выражение всей строке-объекту. Например:

QString source("abc123");

QRegExp("\\d+").exactMatch(source);         // returns false
QRegExp("[a-z]+\\d+").exactMatch(source);   // returns true

QRegularExpression("\\d+").match(source).hasMatch();        // returns true
QRegularExpression("[a-z]+\\d+").match(source).hasMatch();  // returns true

Точное соответствие не отражено в QRegularExpression. Если вы хотите убедиться, что строка-объект точно соответствует регулярному выражению, вы можете обернуть шаблон с помощью функции QRegularExpression::anchoredPattern:

QString source("abc123");

QString pattern("\\d+");
QRegularExpression(pattern).match(source).hasMatch();  // returns true

pattern = QRegularExpression::anchoredPattern(pattern);
QRegularExpression(pattern).match(source).hasMatch();  // returns false

Минимальное соответствие

QRegExp::setMinimal() реализовывало минимальное соответствие, просто изменив жадность квантификаторов (QRegExp не поддерживала ленивые квантификаторы, такие как *?, +?, и т.д.). QRegularExpression поддерживает жадные, ленивые и поглощающие квантификаторы. Опция шаблона QRegularExpression::InvertedGreedinessOption может быть полезна для эмуляции эффектов QRegExp::setMinimal(): при включении она меняет жадность квантификаторов (жадные становятся ленивыми и наоборот).

Разный синтаксис шаблона

Перевод регулярного выражения из QRegExp в QRegularExpression может потребовать изменений в самом шаблоне. Поэтому рекомендуется проверить используемый шаблон с помощью метода QRegularExpression::isValid. Это особенно важно для шаблонов, задаваемых пользователем или не контролируемых разработчиком.

В других случаях, перевод шаблона из QRegExp в QRegularExpression может молча изменить семантику. Поэтому необходимо пересмотреть используемые шаблоны. Наиболее заметными случаями молчаливой несовместимости являются:

  • Для использования шестнадцатеричного escape-последовательности, например, \xHHHH с более чем 2 цифрами, необходимы фигурные скобки. Шаблон, такой как \x2022 , необходимо перевести в \x{2022}, иначе он будет соответствовать пробелу (0x20) за которым следует строка "22". В общем случае настоятельно рекомендуется всегда использовать фигурные скобки с escape-последовательностью \x , независимо от количества указанных цифр.
  • Квантификация типа 0-to-n , как {,n} , необходимо перевести в {0,n} для сохранения семантики. В противном случае шаблон, такой как \d{,3} , фактически будет соответствовать цифре, за которой следует точная строка "{,3}".

Частичное соответствие

При использовании QRegExp::exactMatch(), если точное соответствие не найдено, можно узнать, сколько символов строки-объекта соответствует регулярному выражению, вызвав QRegExp::matchedLength(). Если возвращаемая длина равна длине строки-объекта, можно сделать вывод о нахождении частичного соответствия. QRegularExpression поддерживает частичное соответствие явно с помощью соответствующего перечисления QRegularExpression::MatchType.

Глобальное соответствие

Из-за ограничений API QRegExp невозможно правильно реализовать глобальное соответствие (то есть, как в Perl). В частности, шаблоны, которые могут соответствовать нулю символов (например, "a*"), представляют проблему. QRegularExpression::wildcardToRegularExpression правильно реализует глобальное соответствие Perl, и возвращаемый итератор можно использовать для проверки каждого результата.

Поддержка свойств Юникода

При использовании QRegExp, классы символов, такие как \w, \d, и т.д., сопоставляют символы с соответствующим свойством Юникода: например, \d соответствует любому символу со свойством Юникода Nd (десятичная цифра). Эти классы символов по умолчанию соответствуют только символам ASCII. При использовании QRegularExpression: например, \d соответствует точно символу в диапазоне ASCII от 0 до 9. Можно изменить это поведение, используя опцию шаблона QRegularExpression::UseUnicodePropertiesOption.

Класс QRegExp

В Qt6 класс QRegExp был удален из Qt Core. Если ваше приложение пока нельзя перенести, QRegExp по-прежнему существует в Qt5Compat для обеспечения работоспособности этих кодовых баз. Если вы хотите продолжить использовать QRegExp, вам необходимо связаться с новым модулем Qt5Compat и добавить эту строку в ваш файл qmake .pro файл:

QT += core5compat

Если вы уже перенесли свое приложение или библиотеку в систему сборки cmake, добавьте следующее в свой CMakeList.txt:

PUBLIC_LIBRARIES
    Qt::Core5Compat

QEvent и подклассы

Класс QEvent определил конструктор копирования и оператор присваивания, несмотря на то, что является полиморфным классом. Копирование классов с виртуальными методами может привести к усечению при присваивании объектов разных классов друг другу. Поскольку копирование и присваивание часто происходит неявно, это может привести к трудноотлаживаемым проблемам.

В Qt 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 5 QCoreApplication::quit() был эквивалентен вызову QCoreApplication::exit(). Это просто завершало основной цикл событий.

В Qt 6 этот метод вместо этого попытается закрыть все окна верхнего уровня, разместив событие закрытия. Окна могут отменить процесс завершения, проигнорировав событие.

Вызовите QCoreApplication::exit(), чтобы сохранить безусловное поведение.

QLibraryInfo::location() и QLibraryInfo::Location были устаревшими из-за несогласованного именования. Используйте новый API QLibraryInfo::path() и QLibraryInfo::LibraryPath вместо него.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qtcore-changes-qt6.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API