Die Änderungen an Qt Core
Qt 6 sind das Ergebnis des bewussten Bemühens, das Framework effizienter und benutzerfreundlicher zu gestalten.
Wir versuchen, die Binär- und Quellcodekompatibilität für alle öffentlichen APIs in jeder Version zu erhalten. Einige Änderungen waren jedoch unvermeidlich, um Qt zu einem besseren Framework zu machen.
In diesem Thema fassen wir diese Änderungen in Qt Core zusammen und geben eine Anleitung zu deren Handhabung.
Container-Klassen
QHash, QMultiHash, QSet
qHash()-Signatur
Für benutzerdefinierte Typen sind QHash und QMultiHash darauf angewiesen, dass Sie ein custom qHash() function im gleichen Namespace bereitstellen. In Qt 4 und Qt 5 waren der Rückgabewert und das optionale zweite Argument einer qHash
Funktion vom Typ uint
. In Qt 6 ist es size_t
.
Das heißt, Sie müssen
in ändern.
size_t qHash(MyType x, size_t seed);
Dadurch können QHash, QMultiHash und QSet auf 64-Bit-Plattformen mehr als 2^32 Elemente enthalten.
Stabilität von Referenzen
Die Implementierung von QHash, QMultiHash und QSet in Qt 6 wurde von einem knotenbasierten Ansatz zu einer zweistufigen Nachschlagetabelle geändert. Dieses Design ermöglicht es, den Speicher-Overhead einer Hash-Instanz sehr klein zu halten und gleichzeitig eine gute Leistung zu erzielen.
Eine Verhaltensänderung, die es zu beachten gilt, ist, dass die neue Implementierung keine stabilen Referenzen auf Elemente im Hash bereitstellt, wenn die Tabelle wachsen muss oder wenn Einträge entfernt werden. Anwendungen, die sich auf diese Stabilität verlassen, könnten nun auf undefiniertes Verhalten stoßen.
Abschaffung von QHash::insertMulti
In Qt 5 konnte QHash verwendet werden, um mehrwertige Hashes mit Hilfe von QHash::insertMulti zu erstellen, und QMultiHash wurde von QHash abgeleitet.
In Qt 6 sind beide Typen und Anwendungsfälle eindeutig, und QHash::insertMulti wurde entfernt.
QVector, QList
Vor Qt 6 waren QVector und QList separate Klassen. In Qt 6 sind sie vereinheitlicht: Die QList -Implementierung von Qt 5 ist verschwunden und beide Klassen verwenden stattdessen die aktualisierte QVector -Implementierung. QList ist die Klasse mit der eigentlichen Implementierung und QVector ist ein Alias (Typedef) für QList.
QListDie Funktionen fromVector() und toVector() sowie QVector fromList() und toList() beinhalten in Qt 6 kein Kopieren von Daten mehr. Sie geben nun das Objekt zurück, für das sie aufgerufen wurden.
API-Änderungen
QListDer Größentyp von QVector(und damit auch von ) wurde von int
auf qsizetype
geändert. Zusammen mit dem Größentyp wurden auch die Signaturen aller relevanten Methoden aktualisiert, um qsizetype
zu verwenden. Dadurch kann QList auf 64-Bit-Plattformen mehr als 2^31 Elemente enthalten.
Bei einem Upgrade der Codebasis auf Qt 6 würde diese API-Änderung höchstwahrscheinlich zu Compiler-Warnungen über die Konvertierung von Typen führen. Wenn Sie den folgenden Beispielcode haben:
void myFunction(QList<MyType> &data) { int size = data.size(); // ... const int pos = getInsertPosition(size); data.insert(pos, MyType()); // ... }
müssten Sie ihn aktualisieren, um entweder qsizetype
oder ein auto-Schlüsselwort zu verwenden:
void myFunction(QList<MyType> &data) { auto size = data.size(); // ... const auto pos = getInsertPosition(size); data.insert(pos, MyType()); // ... }
Alternativ können Sie die Typumwandlung verwenden und alles nach int
oder nach qsizetype
umwandeln.
Hinweis: Wenn Sie sowohl mit Qt 5 als auch mit Qt 6 bauen wollen, ist das auto-Schlüsselwort eine gute Lösung, um Signaturunterschiede zwischen den Versionen abzudecken.
Speicher-Layout
QList In Qt 6 gab es mehrere Änderungen im Zusammenhang mit dem Speicherlayout.
In Qt 5 war sizeof(QList<T>)
gleich der Größe eines Zeigers. Jetzt wird die zusätzliche Zeigerumleitung entfernt und die Datenelemente von QList werden direkt im Objekt gespeichert. Standardmäßig sollte sizeof(QList<T>)
gleich der Größe von 3 Zeigern sein.
Gleichzeitig wurde auch das Speicherlayout der Elemente aktualisiert. QList speichert seine Elemente nun immer direkt in der zugewiesenen Speicherregion, im Gegensatz zu Qt 5, wo bestimmte Objekte separat auf dem Heap zugewiesen wurden und Zeiger auf die Objekte stattdessen in QList platziert wurden.
Beachten Sie, dass letzteres vor allem große Objekte betrifft. Um das Verhalten von Qt 5 zu erhalten, könnten Sie Ihre Objekte in Smart Pointers verpacken und diese Smart Pointer direkt in QList speichern. In diesem Fall wäre der Typ Ihrer QList QList<MySmartPointer<MyLargeObject>>
im Gegensatz zu QList<MyLargeObject>
in Qt 5.
Stabilität von Referenzen
Es wurden mehrere Änderungen an der QVector/QList Implementierung vorgenommen. Die mit QVector zusammenhängende Änderung ist: Das Einfügen am Anfang wurde optimiert (ähnlich wie QList in Qt 5). Die QList bezogene Änderung ist: das Speicherlayout für die Elemente wurde vereinfacht.
Wichtig: Diese Änderungen haben Auswirkungen auf die Stabilität von Referenzen. In Qt 6 sollten Sie davon ausgehen, dass jede Methode, die die Größe oder Kapazität verändert, alle Referenzen ungültig macht, auch wenn QList nicht implizit freigegeben ist. Ausnahmen von dieser Regel sind explizit dokumentiert.
Anwendungen, die auf eine bestimmte Referenzstabilität angewiesen sind, können bei einem Upgrade auf Qt 6 auf undefiniertes Verhalten stoßen. Sie sollten besonders auf Fälle achten, in denen ursprünglich QVector oder QList mit einem nicht C-kompatiblen Array-Layout verwendet wurden.
Ansichtsklassen in Qt6
Allgemeiner Überblick
Es gibt mehrere neue View
Klassen, die mit Qt6 kommen. Es gibt die bereits existierende QStringView, die nun von QByteArrayView begleitet wird, gefolgt von einer spezialisierten QUtf8StringView und einer universelleren QAnyStringView.
Einführung in die View-Klassen am Beispiel von QStringView
Die Klasse QStringView bietet eine einheitliche Ansicht auf UTF-16-Strings mit einer schreibgeschützten Teilmenge der QString API. Im Gegensatz zu QString, das seine eigene Kopie der Zeichenkette (möglicherweise mit Referenz) behält, bietet QStringView eine Ansicht einer Zeichenkette, die an anderer Stelle gespeichert ist.
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
Die Zeichenkette "Hello."
ist in der Binärdatei gespeichert und wird zur Laufzeit nicht zugewiesen. view
ist nur eine Sicht auf die Zeichenkette "Hello."
, daher muss keine Kopie erstellt werden. Wenn wir eine QStringView kopieren, beobachtet viewToView
dieselbe Zeichenkette, die die kopierte view
gerade beobachtet. Das bedeutet, dass viewToView
weder eine Kopie noch ein atomares Inkrement erstellen muss. Es handelt sich um Sichten auf die bestehende Zeichenkette "Hello."
.
Ansichten als Funktionsargument
Views sollten als Wert übergeben werden, nicht als Referenz-zu-Konst.
void myfun1(QStringView sv); // preferred void myfun2(const QStringView &sv); // compiles and works, but slower
Funktionen zur Manipulation von Ansichten
QStringView unterstützt Funktionen, mit denen wir die Ansicht der Zeichenkette manipulieren können. Dadurch kann die Ansicht geändert werden, ohne eine Teilkopie der angezeigten Zeichenkette zu erstellen.
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);
Nicht null-terminierte Strings und Strings, die '\0'
QStringView unterstützt sowohl null-terminierte als auch nicht-null-terminierte Zeichenketten. Der Unterschied ergibt sich aus der Art und Weise, wie Sie die QStringView initialisieren:
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'
Eigentumsmodell von Ansichten
Da views
nicht Eigentümer des Speichers ist, auf den sie verweisen, muss darauf geachtet werden, dass die referenzierten Daten (z. B. im Besitz einer QString) die view
auf allen Codepfaden überdauern.
QStringView sayHello() { QString hello("Hello."); return QStringView{ hello }; // hello gets out of scope and destroyed } void main() { QStringView hello{ sayHello() }; qDebug() << hello; // undefined behavior }
Die Konvertierung einer QStringView in QString
QStringView wird weder implizit noch explizit in eine QString konvertiert, kann aber eine Deep Copy der Daten erstellen:
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 }
Wichtige Hinweise
Durch die Nutzung der neuen View-Klassen kann man in vielen Anwendungsfällen einen großen Leistungsschub erreichen. Es ist jedoch wichtig zu wissen, dass es einige Vorbehalte geben kann. Daher ist es wichtig, sich daran zu erinnern:
- Views sollten als Wert übergeben werden, nicht als Referenz-zu-Konst.
- Die Konstruktion eines Views mit einer negativen Länge ist ein undefiniertes Verhalten.
- Es muss darauf geachtet werden, dass die referenzierten Daten (z.B. im Besitz einer QString) den View auf allen Codepfaden überdauern.
String-bezogene Klassen
Die Klasse QStringView
Ab Qt6 wird allgemein empfohlen, QStringView anstelle von QStringRef
zu verwenden. QStringView referenziert einen zusammenhängenden Teil eines UTF-16 Strings, den es nicht besitzt. Sie fungiert als Schnittstellentyp für alle Arten von UTF-16-Strings, ohne dass zuerst ein QString konstruiert werden muss. Die Klasse QStringView stellt fast alle Nur-Lese-Methoden von QString und der bereits existierenden Klasse QStringRef
zur Verfügung.
Hinweis: Es muss darauf geachtet werden, dass die referenzierten String-Daten (z.B. im Besitz einer QString) die QStringView auf allen Codepfaden überdauern.
Hinweis: Wenn eine QStringView eine QString umhüllt, muss darauf geachtet werden, dass im Gegensatz zu QStringRef
QStringView den internen Datenzeiger nicht aktualisiert, sobald die QString Daten verschoben werden.
QString string = ...; QStringView view{string}; // Appending something very long might cause a relocation and will // ultimately result in a garbled QStringView. string += ...;
Die QStringRef-Klasse
In Qt6 wurde QStringRef aus Qt Core entfernt. Um die Portierung bestehender Anwendungen zu erleichtern, ohne die gesamte Code-Basis anzutasten, verschwand die Klasse QStringRef
nicht vollständig, sondern wurde in das Modul Qt5Compat verschoben. Wenn Sie QStringRef
weiter verwenden möchten, lesen Sie bitte den Abschnitt Verwendung des Qt5Compat-Moduls.
Leider konnten einige Methoden von QString, die ein QStringRef
zurückgeben, nicht nach Qt5Compat verschoben werden. Daher kann eine manuelle Portierung erforderlich sein. Wenn Ihr Code eine oder mehrere der folgenden Funktionen verwendet, müssen Sie diese portieren, um QStringView oder QStringTokenizer zu verwenden. Es wird auch empfohlen, QStringView::tokenize statt QStringView::split für leistungsrelevanten Code zu verwenden.
Ändern Sie Code, der QStringRef
verwendet:
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;
zu:
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 und verwandte Klassen
In Qt 6 erbt QRecursiveMutex nicht mehr von QMutex. Diese Änderung wurde vorgenommen, um die Leistung von QMutex und QRecursiveMutex zu verbessern.
Aufgrund dieser Änderungen wurde das QMutex::RecursionMode enum entfernt, und QMutexLocker ist nun eine Template-Klasse, die sowohl auf QMutex als auch auf QRecursiveMutex arbeiten kann.
QFuture und verwandte Klassen
Die Klasse QFuture
Um eine unbeabsichtigte Verwendung von QFuture zu vermeiden, wurden in Qt 6 einige Änderungen an der QFuture API vorgenommen, die zu Brüchen in der Quellcodekompatibilität führen können.
Implizite Konvertierungen zwischen QFuture und anderen Typen
Die Konvertierung von QFuture<T>
nach T
wurde deaktiviert. Der Casting-Operator rief QFuture::result() auf, was zu undefiniertem Verhalten führen kann, wenn der Benutzer die Ergebnisse von QFuture über QFuture::takeResult() verschoben hat, bevor er versucht, die Konvertierung durchzuführen. Verwenden Sie die Methoden QFuture::result() oder QFuture::takeResult() explizit, wenn Sie QFuture<T>
in T
konvertieren müssen.
Die implizite Konvertierung von QFuture<T>
nach QFuture<void>
wurde ebenfalls deaktiviert. Wenn Sie die Konvertierung wirklich durchführen wollen, verwenden Sie den expliziten QFuture<void>(const QFuture<T> &)
Konstruktor:
Gleichheitsoperatoren
Die Gleichheitsoperatoren von QFuture wurden entfernt. Sie verglichen die zugrundeliegenden d-Zeiger, anstatt die Ergebnisse zu vergleichen, was nicht das ist, was die Benutzer erwarten. Wenn Sie QFuture Objekte vergleichen müssen, verwenden Sie die Methoden QFuture::result()
oder QFuture::takeResult()
. Zum Beispiel:
QFuture<int> future1 = ...; QFuture<int> future2 = ...; if (future1.result() == future2.result()) // ...
Änderungen am Verhalten von QFuture und QFutureWatcher
In Qt 6 gab es einige Verbesserungen an QFuture und QFutureWatcher, die zu den folgenden Verhaltensänderungen führten:
- Nach dem Anhalten von QFuture oder QFutureWatcher (durch den Aufruf von
pause()
odersetPaused(true)
), wird QFutureWatcher nicht sofort aufhören, Fortschritts- und Ergebnisbereitschaftssignale zu liefern. Zum Zeitpunkt der Unterbrechung können noch Berechnungen im Gange sein, die nicht gestoppt werden können. Signale für solche Berechnungen können auch nach der Pause geliefert werden, anstatt verschoben zu werden und erst nach der nächsten Wiederaufnahme gemeldet zu werden. Um benachrichtigt zu werden, wenn die Pause tatsächlich wirksam wurde, kann das Signal QFutureWatcher::suspended() verwendet werden. Darüber hinaus gibt es neue MethodenisSuspending()
undisSuspended()
, um zu prüfen, ob QFuture gerade angehalten wird oder sich bereits im angehaltenen Zustand befindet. Beachten Sie, dass aus Konsistenzgründen sowohl für QFuture als auch für QFutureWatcher die pause-bezogenen APIs veraltet sind und durch ähnliche Methoden ersetzt wurden, die stattdessen "suspend" im Namen tragen. - QFuture::waitForFinished() wartet nun, bis QFuture tatsächlich den Zustand "finished" erreicht hat, anstatt sich zu beenden, sobald es sich nicht im Zustand "running" befindet. Dies verhindert, dass
waitForFinished()
sofort beendet wird, wenn zum Zeitpunkt des Aufrufs der Future noch nicht gestartet ist. Das gleiche gilt für QFutureWatcher::waitForFinished(). Diese Änderung hat keinen Einfluss auf das Verhalten von Code, der QFuture mit QtConcurrent verwendet hat. Nur der Code, der es mit dem undokumentiertenQFutureInterface
verwendet hat, kann betroffen sein. - QFutureWatcher::isFinished() spiegelt nun den Endzustand von QFuture wider, anstatt false zurückzugeben, bis QFutureWatcher::finished() gesendet wurde.
Die QPromise-Klasse
In Qt 6 sollte die neue Klasse QPromise anstelle des inoffiziellen QFutureInterface als "Setter"-Pendant von QFuture verwendet werden.
IO-Klassen
Die QProcess-Klasse
In Qt 6 wurde die Überladung QProcess::start(), die einen einzelnen Befehlsstring interpretiert, indem sie ihn in Programmname und Argumente aufspaltet, in QProcess::startCommand() umbenannt. Es gibt jedoch eine QProcess::start()-Überladung, die eine einzelne Zeichenkette sowie eine QStringList für Argumente akzeptiert. Da der QStringList Parameter standardmäßig eine leere Liste ist, wird vorhandener Code, der nur eine Zeichenkette übergibt, weiterhin kompiliert, aber der Prozess wird nicht ausgeführt, wenn es sich um eine vollständige Befehlszeichenkette handelt, die Argumente enthält.
Qt 5.15 hat Verwerfungswarnungen für die entsprechenden Überladungen eingeführt, um es einfach zu machen, bestehenden Code zu entdecken und zu aktualisieren:
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() und der Typ Q_PID wurden entfernt; verwenden Sie stattdessen QProcess::processId(), um den nativen Prozessbezeichner zu erhalten. Code, der native Win32-APIs verwendet, um auf die Daten in Q_PID als Win32 PROCESS_INFORMATION
struct zuzugreifen, wird nicht mehr unterstützt.
Meta-Typ-System
Die Klasse QVariant
QVariant
wurde umgeschrieben, um QMetaType
für alle ihre Operationen zu verwenden. Dies impliziert Verhaltensänderungen in einigen Methoden:
QVariant::isNull()
QVariant gibt jetzt nur nochtrue
zurück, wennQVariant
leer ist oder einnullptr
enthält. In Qt 5 wurde auch für Klassen in qtbase, die selbst eineisNull
Methode hatten, true zurückgegeben, wenn diese true zurückgab. Code, der sich auf das alte Verhalten verlässt, muss prüfen, ob der enthaltene Wert isNull zurückgibt - allerdings ist es unwahrscheinlich, dass solcher Code in der Praxis vorkommt, daisNull()
selten die Eigenschaft ist, an der man interessiert ist (vergleicheQString::isEmpty()
/isNull()
undQTime::isValid
/isNull
).QVariant::operator==
verwendetQMetaType::equals
in Qt 6. Daher werden einige grafische Typen wieQPixmap
,QImage
undQIcon
niemals gleich verglichen. Außerdem werden Fließkommazahlen, die inQVariant
gespeichert sind, nicht mehr mitqFuzzyCompare
verglichen, sondern es werden exakte Vergleiche verwendet.
Außerdem wurden QVariant::operator<, QVariant::operator<=, QVariant::operator> und QVariant::operator>= entfernt, da verschiedene Varianten nicht immer geordnet werden können. Das bedeutet auch, dass QVariant nicht mehr als Schlüssel in einem QMap verwendet werden kann.
Die QMetaType-Klasse
In Qt 6 erfolgt die Registrierung von Komparatoren sowie von QDebug und QDataStream Streaming-Operatoren automatisch. Folglich gibt es QMetaType::registerEqualsComparator()
, QMetaType::registerComparators()
, qRegisterMetaTypeStreamOperators()
und QMetaType::registerDebugStreamOperator()
nicht mehr. Die Aufrufe dieser Methoden müssen bei der Portierung nach Qt 6 entfernt werden.
Typregistrierung
Typen, die in Q_PROPERTY
verwendet werden, haben ihren Meta-Typ in QMetaObject
der Klasse gespeichert. Dies erfordert, dass die Typen vollständig sind, wenn moc sie sieht, was zu Kompilierungsfehlern in Code führen kann, der in Qt 5 funktionierte. Es gibt drei Möglichkeiten, dieses Problem zu beheben:
- Binden Sie den Header ein, der den Typ definiert.
- Anstatt ein Include zu verwenden, benutzen Sie das
Q_MOC_INCLUDE
Makro. Dies hilft, wenn das Einbinden des Headers eine zyklische Abhängigkeit verursachen würde oder wenn es die Kompilierung verlangsamen würde. - Wenn der Header in der cpp-Datei vorhanden ist, die die Klasse implementiert, ist es auch möglich, die moc-generierte Datei dort einzubinden.
Klassen für reguläre Ausdrücke
Die Klasse QRegularExpression
In Qt 6 wurde der Typ QRegExp
in das Modul Qt5Compat ausgelagert und alle Qt-APIs, die ihn verwenden, wurden aus anderen Modulen entfernt. Client-Code, der diesen Typ verwendet, kann portiert werden, um stattdessen QRegularExpression zu verwenden. Da QRegularExpression bereits in Qt 5 vorhanden ist, kann dies vor der Migration auf Qt 6 durchgeführt und getestet werden.
Die in Qt 5 eingeführte Klasse QRegularExpression implementiert Perl-kompatible reguläre Ausdrücke und ist eine große Verbesserung gegenüber QRegExp in Bezug auf die angebotenen APIs, die unterstützte Mustersyntax und die Ausführungsgeschwindigkeit. Der größte Unterschied besteht darin, dass QRegularExpression einfach einen regulären Ausdruck enthält, der nicht verändert wird, wenn eine Übereinstimmung angefordert wird. Stattdessen wird ein QRegularExpressionMatch Objekt zurückgegeben, um das Ergebnis einer Übereinstimmung zu prüfen und die erfasste Teilzeichenkette zu extrahieren. Das Gleiche gilt für den globalen Abgleich und QRegularExpressionMatchIterator.
Weitere Unterschiede werden im Folgenden beschrieben.
Hinweis: QRegularExpression unterstützt nicht alle Funktionen, die in Perl-kompatiblen regulären Ausdrücken verfügbar sind. Die bemerkenswerteste ist die Tatsache, dass doppelte Namen für die Erfassung von Gruppen nicht unterstützt werden, und ihre Verwendung kann zu undefiniertem Verhalten führen. Dies könnte sich in einer zukünftigen Version von Qt ändern.
Unterschiedliche Mustersyntax
Die Portierung eines regulären Ausdrucks von QRegExp auf QRegularExpression kann Änderungen am Muster selbst erfordern.
In bestimmten Szenarien war QRegExp zu nachsichtig und akzeptierte Muster, die bei Verwendung von QRegularExpression einfach ungültig sind. Diese sind leicht zu erkennen, da die mit diesen Mustern erstellten QRegularExpression Objekte nicht gültig sind (siehe QRegularExpression::isValid()).
In anderen Fällen kann sich die Semantik eines Musters, das von QRegExp auf QRegularExpression portiert wurde, stillschweigend ändern. Daher ist es notwendig, die verwendeten Muster zu überprüfen. Die bemerkenswertesten Fälle von stiller Inkompatibilität sind:
- Geschweifte Klammern sind erforderlich, um einen hexadezimalen Escape wie
\xHHHH
mit mehr als 2 Ziffern zu verwenden. Ein Muster wie\x2022
muss auf\x{2022}
portiert werden, oder es passt auf ein Leerzeichen (0x20
) gefolgt von der Zeichenkette"22"
. Im Allgemeinen ist es sehr empfehlenswert, immer geschweifte Klammern mit dem\x
Escape zu verwenden, unabhängig von der Anzahl der angegebenen Ziffern. - Eine 0-zu-n-Quantifizierung wie
{,n}
muss auf{0,n}
portiert werden, um die Semantik zu erhalten. Andernfalls würde ein Muster wie\d{,3}
mit einer Ziffer übereinstimmen, gefolgt von der exakten Zeichenfolge"{,3}"
. - QRegExp führt standardmäßig einen Unicode-kompatiblen Abgleich durch, während QRegularExpression eine separate Option erfordert; siehe unten für weitere Details.
- c{.} in QRegExp passt standardmäßig auf alle Zeichen, einschließlich des Zeilenumbruchs. QRegularExpression schließt den Zeilenumbruch standardmäßig aus. Um den Zeilenumbruch einzuschließen, setzen Sie die Option QRegularExpression::DotMatchesEverythingOption pattern.
Einen Überblick über die von QRegularExpression unterstützte Syntax regulärer Ausdrücke finden Sie in der Man Page pcrepattern(3), die die von PCRE (der Referenzimplementierung von Perl-kompatiblen regulären Ausdrücken) unterstützte Mustersyntax beschreibt.
Die Portierung von QRegExp::exactMatch()
QRegExp::exactMatch() diente zwei Zwecken: Sie glich einen regulären Ausdruck exakt mit einer Zeichenkette ab und implementierte eine teilweise Übereinstimmung.
Portierung von QRegExp's exaktem Abgleich
Der exakte Abgleich gibt an, ob der reguläre Ausdruck mit der gesamten Zeichenkette übereinstimmt. Die Klassen ergeben zum Beispiel die Betreffzeichenkette "abc123"
:
QRegExp::exactMatch() | QRegularExpressionMatch::hasMatch() | |
---|---|---|
"\\d+" | falsch | wahr |
"[a-z]+\\d+" | wahr | wahr |
Eine exakte Übereinstimmung wird in QRegularExpression nicht wiedergegeben. Wenn Sie sicher sein wollen, dass die Betreffzeichenkette genau mit dem regulären Ausdruck übereinstimmt, können Sie das Muster mit der Funktion QRegularExpression::anchoredPattern() einschließen:
QString p("a .*|pattern"); // re matches exactly the pattern string p QRegularExpression re(QRegularExpression::anchoredPattern(p));
Portierung von QRegExp's partiellem Matching
Wenn bei der Verwendung von QRegExp::exactMatch() keine exakte Übereinstimmung gefunden wurde, konnte man durch den Aufruf von QRegExp::matchedLength() immer noch herausfinden, wie viel der Betreffzeichenkette mit dem regulären Ausdruck übereinstimmte. Wenn die zurückgegebene Länge gleich der Länge der Zeichenkette war, konnte man daraus schließen, dass eine teilweise Übereinstimmung gefunden wurde.
QRegularExpression unterstützt partielle Übereinstimmungen explizit durch die entsprechende QRegularExpression::MatchType.
Globaler Abgleich
Aufgrund der Beschränkungen der QRegExp API war es nicht möglich, globale Übereinstimmungen korrekt zu implementieren (d.h. so, wie es Perl tut). Insbesondere Muster, die mit 0 Zeichen übereinstimmen können (wie "a*"
), sind problematisch.
QRegularExpression::globalMatch() implementiert den globalen Abgleich in Perl korrekt, und der zurückgegebene Iterator kann verwendet werden, um jedes Ergebnis zu untersuchen.
Wenn Sie zum Beispiel einen Code wie:
QString subject("the quick fox"); int offset = 0; QRegExp re("(\\w+)"); while ((offset = re.indexIn(subject, offset)) != -1) { offset += re.matchedLength(); // ... }
Sie können ihn umschreiben als:
QString subject("the quick fox"); QRegularExpression re("(\\w+)"); QRegularExpressionMatchIterator i = re.globalMatch(subject); while (i.hasNext()) { QRegularExpressionMatch match = i.next(); // ... }
Unterstützung von Unicode-Eigenschaften
Bei Verwendung von QRegExp passen Zeichenklassen wie \w
, \d
usw. auf Zeichen mit der entsprechenden Unicode-Eigenschaft: \d
passt beispielsweise auf jedes Zeichen mit der Unicode-Eigenschaft Nd
(Dezimalziffer).
Diese Zeichenklassen passen standardmäßig nur auf ASCII-Zeichen, wenn QRegularExpression verwendet wird: \d
passt beispielsweise genau auf ein Zeichen im 0-9
ASCII-Bereich. Es ist möglich, dieses Verhalten mit der Option QRegularExpression::UseUnicodePropertiesOption pattern zu ändern.
Wildcard-Abgleich
Es gibt keine direkte Möglichkeit, Wildcard-Matches in QRegularExpression durchzuführen. QRegularExpression::wildcardToRegularExpression() bietet jedoch die Möglichkeit, glob-Muster in einen Perl-kompatiblen regulären Ausdruck zu übersetzen, der für diesen Zweck verwendet werden kann.
Wenn Sie zum Beispiel einen Code wie:
Sie können ihn umschreiben als:
auto wildcard = QRegularExpression(QRegularExpression::wildcardToRegularExpression("*.txt"));
Bitte beachten Sie jedoch, dass einige Shell-ähnliche Platzhaltermuster möglicherweise nicht so übersetzt werden, wie Sie es erwarten. Der folgende Beispielcode bricht ab, wenn er einfach mit der oben erwähnten Funktion umgewandelt wird:
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
Das liegt daran, dass der reguläre Ausdruck, der von QRegularExpression::wildcardToRegularExpression() zurückgegeben wird, standardmäßig vollständig verankert ist. Um einen regulären Ausdruck zu erhalten, der nicht verankert ist, übergeben Sie QRegularExpression::UnanchoredWildcardConversion als Konvertierungsoptionen:
QRegularExpression re3(QRegularExpression::wildcardToRegularExpression( "*/files/*", QRegularExpression::UnanchoredWildcardConversion)); re3.match(fp1).hasMatch(); // returns true re3.match(fp2).hasMatch(); // returns true
Minimaler Abgleich
QRegExp::setMinimal() implementiert eine minimale Anpassung, indem es einfach die Gier der Quantoren umkehrt (QRegExp unterstützt keine faulen Quantoren, wie *?
, +?
, usw.). QRegularExpression unterstützt stattdessen gierige, faule und possessive Quantoren. Die Option QRegularExpression::InvertedGreedinessOption pattern kann nützlich sein, um die Auswirkungen von QRegExp::setMinimal() zu emulieren: Wenn sie aktiviert ist, kehrt sie die Gierigkeit von Quantoren um (gierige Quantoren werden faul und umgekehrt).
Caret-Modi
Die Option QRegularExpression::AnchorAtOffsetMatchOption match kann verwendet werden, um das Verhalten von QRegExp::CaretAtOffset zu emulieren. Für die anderen Modi von QRegExp::CaretMode gibt es keine Entsprechung.
Die QRegExp-Klasse
In Qt6 wurde QRegExp aus Qt Core entfernt. Falls Ihre Anwendung nicht portiert werden kann, gibt es noch QRegExp
in Qt5Compat, um diese Code-Basen am Laufen zu halten. Wenn Sie QRegExp
weiter verwenden wollen, sehen Sie unter Verwendung des Qt5Compat-Moduls nach.
QEvent und Unterklassen
Die Klasse QEvent definiert einen Kopierkonstruktor und einen Zuweisungsoperator, obwohl sie eine polymorphe Klasse ist. Das Kopieren von Klassen mit virtuellen Methoden kann zu Slicing führen, wenn Objekte aus verschiedenen Klassen einander zugewiesen werden. Da das Kopieren und Zuweisen oft implizit geschieht, kann dies zu schwer zu behebenden Problemen führen.
In Qt 6 wurden der Kopierkonstruktor und der Zuweisungsoperator für QEvent Unterklassen geschützt, um implizites Kopieren zu verhindern. Wenn Sie Ereignisse kopieren müssen, verwenden Sie die Methode clone, die eine auf dem Heap allozierte Kopie des QEvent Objekts zurückgibt. Stellen Sie sicher, dass Sie den Klon löschen, z.B. mit std::unique_ptr, es sei denn, Sie posten ihn (in diesem Fall wird Qt ihn löschen, sobald er ausgeliefert wurde).
In Ihren QEvent Unterklassen überschreiben Sie clone() und deklarieren den geschützten und standardmäßig implementierten Kopierkonstruktor und Zuweisungsoperator wie folgt:
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 };
Beachten Sie, dass Sie eine eigene Kopiersemantik implementieren müssen, wenn Ihre MyEvent-Klasse Speicher zuweist (z. B. durch ein Pointer-to-Implementation-Muster).
Serialisierungsklassen
In Qt 6 wurden die QJsonDocument Methoden zur Konvertierung in/aus dem alten JSON-Binärformat von Qt zugunsten des standardisierten CBOR-Formats entfernt. Qt JSON-Typen können in Qt CBOR-Typen konvertiert werden, die wiederum in das CBOR-Binärformat serialisiert werden können und umgekehrt. Siehe z.B. QCborValue::fromJsonValue() und QCborValue::toJsonValue().
Wenn Sie dennoch das binäre JSON-Format verwenden müssen, können Sie die im Qt5Compat-Modul bereitgestellten Ersetzungen verwenden. Sie sind im QBinaryJson Namensraum zu finden. Siehe Verwendung des Qt5Compat-Moduls, um herauszufinden, wie Sie das Modul in Ihrer Anwendung verwenden können.
Andere Klassen
In Qt 5 war QCoreApplication::quit() gleichbedeutend mit dem Aufruf von QCoreApplication::exit(). Dies beendete lediglich die Hauptereignisschleife.
In Qt 6 versucht die Methode stattdessen, alle Fenster der obersten Ebene zu schließen, indem sie ein close-Ereignis sendet. Es steht den Fenstern frei, den Schließvorgang abzubrechen, indem sie das Ereignis ignorieren.
Rufen Sie QCoreApplication::exit() auf, um das nicht-bedingte Verhalten beizubehalten.
QLibraryInfo::location() und QLibraryInfo::Location wurden aufgrund inkonsistenter Namensgebung veraltet. Verwenden Sie stattdessen die neuen APIs QLibraryInfo::path() und QLibraryInfo::LibraryPath.
Qt State Machine Das Framework
Qt State Machine wurde in das Modul Qt SCXML verschoben (das bald in Qt State Machines umbenannt wird) und ist daher nicht mehr Teil von Qt Core. Es gab nur sehr wenige Querabhängigkeiten innerhalb von Qt Core, was letztendlich zu dieser Entscheidung führte.
Verwendung des Qt5Compat-Moduls
Um das Qt5Compat-Modul zu verwenden, müssen Sie dessen Header in Ihrem Include-Pfad einbinden und gegen seine Bibliothek linken. Wenn Sie qmake verwenden, fügen Sie das Folgende zu Ihrer .pro
Datei hinzu:
QT += core5compat
Wenn Sie Ihre Anwendung oder Bibliothek mit cmake erstellen, fügen Sie Folgendes zu Ihrem CMakeList.txt
hinzu:
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.