Zerlegen wir diese Behauptungen in tatsächlich messbare Phänomene:
- Leichter: Qt-Container verbrauchen weniger Speicher als STL-Container
- Sicherer: Qt-Behälter haben weniger Gelegenheit, unsachgemäß verwendet zu werden
- Einfacher: Qt-Container stellen eine geringere intellektuelle Belastung dar
Einfacher
In diesem Zusammenhang wird behauptet, dass die Iteration im Java-Stil irgendwie "einfacher" als der STL-Stil ist und daher Qt aufgrund dieser zusätzlichen Schnittstelle einfacher zu verwenden ist.
Java-Stil:
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
STL-Stil:
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
qDebug << *i;
Der Java-Iterator-Stil hat den Vorteil, dass er etwas kleiner und sauberer ist. Das Problem ist, dass dies nicht mehr der STL-Stil ist.
C ++ 11 STL-Stil
for( auto i = list.begin(); i != list.end(); ++i)
qDebug << *i;
oder
C ++ 11 für jeden Stil
for (QString i : list)
qDebug << i;
Das ist so drastisch einfach, dass es keinen Grund gibt, jemals etwas anderes zu verwenden (es sei denn, Sie unterstützen C ++ 11 nicht).
Mein Favorit ist jedoch:
BOOST_FOREACH(QString i, list)
{
qDebug << i;
}
Wie wir sehen können, bringt uns diese Schnittstelle nichts weiter als eine zusätzliche Schnittstelle, zusätzlich zu einer bereits schlanken, optimierten und modernen Benutzeroberfläche. Hinzufügen einer unnötigen Abstraktionsebene über eine bereits stabile und verwendbare Schnittstelle? Nicht meine Vorstellung von "einfacher".
Qt foreach- und Java-Schnittstellen erhöhen den Overhead. Sie kopieren die Struktur und bieten eine unnötige Indirektionsebene. Dies scheint nicht viel zu sein, aber warum sollte eine zusätzliche Overhead-Schicht hinzugefügt werden, um eine nicht allzu viel einfachere Benutzeroberfläche bereitzustellen? Java verfügt über diese Schnittstelle, da Java keine Operatorüberladung aufweist. C ++ tut es.
Sicherer
Die Rechtfertigung, die Qt gibt, ist das implizite Austauschproblem, das weder implizit noch ein Problem ist. Es beinhaltet jedoch das Teilen.
QVector<int> a, b;
a.resize(100000); // make a big vector filled with 0.
QVector<int>::iterator i = a.begin();
// WRONG way of using the iterator i:
b = a;
/*
Now we should be careful with iterator i since it will point to shared data
If we do *i = 4 then we would change the shared instance (both vectors)
The behavior differs from STL containers. Avoid doing such things in Qt.
*/
Erstens ist dies nicht implizit; Sie weisen explizit einen Vektor einem anderen zu. Die STL-Iteratorspezifikation zeigt deutlich an, dass Iteratoren zum Container gehören. Daher haben wir eindeutig einen gemeinsam genutzten Container zwischen b und a eingeführt. Zweitens ist dies kein Problem. Solange alle Regeln der Iteratorspezifikation eingehalten werden, wird absolut nichts schief gehen. Das einzige Mal, wenn etwas schief geht, ist hier:
b.clear(); // Now the iterator i is completely invalid.
Qt gibt dies so an, als ob es etwas bedeutet, so wie ein Problem de novo aus diesem Szenario entsteht. Das tut es nicht. Der Iterator ist ungültig und funktioniert genau wie alles, auf das von mehreren nicht zusammenhängenden Bereichen aus zugegriffen werden kann. Tatsächlich wird dies bei Iteratoren im Java-Stil in Qt leicht auftreten, da es stark auf implizites Teilen angewiesen ist, das ein Antimuster ist, wie hier dokumentiert , und in vielen anderen Bereichen . Es scheint besonders seltsam, diese "Optimierung" in einem Framework einzusetzen, das sich immer mehr in Richtung Multithreading bewegt, aber das ist Marketing für Sie.
Feuerzeug
Dieser ist etwas kniffliger. Die Verwendung von Copy-On-Write- und impliziten Sharing- und Wachstumsstrategien macht es sehr schwierig, tatsächlich Garantien dafür zu geben, wie viel Speicher Ihr Container zu einem bestimmten Zeitpunkt verbraucht. Dies ist anders als bei der STL, die Ihnen starke algorithmische Garantien bietet.
Wir wissen, dass die minimale Grenze des verschwendeten Raums für einen Vektor die Quadratwurzel der Länge des Vektors ist , aber es scheint keine Möglichkeit zu geben, dies in Qt zu implementieren. Die verschiedenen "Optimierungen", die sie unterstützen, würden diese sehr wichtige platzsparende Funktion ausschließen. Die STL benötigt diese Funktion nicht (und die meisten verwenden ein doppeltes Wachstum, was verschwenderischer ist), aber es ist wichtig zu beachten, dass Sie diese Funktion bei Bedarf zumindest implementieren können.
Gleiches gilt für doppelt verknüpfte Listen, bei denen die XOr-Verknüpfung verwendet werden könnte, um den verwendeten Speicherplatz drastisch zu reduzieren. Auch dies ist bei Qt aufgrund der Anforderungen an Wachstum und COW nicht möglich.
COW kann in der Tat etwas leichter machen, aber auch aufdringliche Container, wie sie durch Boost unterstützt werden , und Qt haben diese in früheren Versionen häufig verwendet, aber sie werden nicht mehr so oft verwendet, weil sie schwer zu verwenden, unsicher und belastend sind auf dem Programmierer. COW ist eine viel weniger aufdringliche Lösung, aber aus den oben genannten Gründen unattraktiv.
Es gibt keinen Grund, warum Sie keine STL-Container mit denselben oder geringeren Speicherkosten als die Container von Qt verwenden könnten, mit dem zusätzlichen Vorteil, dass Sie tatsächlich wissen, wie viel Speicher Sie zu einem bestimmten Zeitpunkt verschwenden werden. Es ist leider unmöglich, die beiden in Bezug auf die Rohspeichernutzung zu vergleichen, da solche Benchmarks in verschiedenen Anwendungsfällen sehr unterschiedliche Ergebnisse zeigen würden, was genau die Art von Problem ist, für deren Behebung die STL entwickelt wurde.
Abschließend
Vermeiden Sie die Verwendung von Qt-Containern, wann immer dies möglich ist, ohne dass Kopierkosten anfallen, und verwenden Sie nach Möglichkeit eine Iteration vom Typ STL (möglicherweise über einen Wrapper oder die neue Syntax).