Ich habe angefangen, std::(w)string
ausschließlich die STL-Container zu verwenden und in / aus den Qt-Äquivalenten zu konvertieren, aber ich habe bereits zu gewechselt QString
und stelle fest, dass ich immer mehr Qt-Container verwende.
Wenn es um Strings geht, QString
bietet es eine viel umfassendere Funktionalität als std::basic_string
Unicode und ist vollständig Unicode-fähig. Es bietet auch eine effiziente COW-Implementierung , auf die ich mich stark verlassen habe.
Qts Container:
- bieten die gleiche COW-Implementierung wie in
QString
, was äußerst nützlich ist, wenn das Qt- foreach
Makro (das eine Kopie erstellt) und Metatypen oder Signale und Slots verwendet werden.
- kann Iteratoren im STL-Stil oder Iteratoren im Java-Stil verwenden
- sind streambar mit
QDataStream
- werden häufig in der Qt-API verwendet
- haben eine stabile Implementierung über Betriebssysteme hinweg. Eine STL-Implementierung muss dem C ++ - Standard entsprechen, kann aber ansonsten frei tun, was sie will (siehe die
std::string
COW-Kontroverse). Einige STL-Implementierungen sind besonders schlecht.
- Stellen Sie Hashes bereit, die nur verfügbar sind, wenn Sie TR1 verwenden
Die QTL hat eine andere Philosophie als die STL, die von J. Blanchette gut zusammengefasst wird: "Während die Container von STL für die Rohgeschwindigkeit optimiert sind, wurden die Containerklassen von Qt sorgfältig entwickelt, um Komfort, minimalen Speicherbedarf und minimale Codeerweiterung zu bieten."
Der obige Link enthält weitere Details zur Implementierung der QTL und zu den verwendeten Optimierungen.
QList<double>
auf einer 32-Bit-Architektur zur Speichernutzung, um sich selbst davon zu überzeugen.QVector
stattQList
. Es gibt ziemlich viele Qt-Erklärungen, dass QList zum Speichern von Zeigern auf Objekte entwickelt wurde. So wird jedes dynamisch erstellte Doppelelement und der Zeiger auf dieses Element gespeichertQList
. QList ist als "mittlerer" Container zwischen Vektor und verknüpfter Liste konzipiert. Es ist nicht für speicher- / leistungskritische Fälle konzipiert.Diese Frage ist schwer zu beantworten. Es kann wirklich auf ein philosophisches / subjektives Argument hinauslaufen.
Davon abgesehen ...
Ich empfehle die Regel "Wenn in Rom ... mach wie die Römer"
Das heißt, wenn Sie sich im Qt-Land befinden, codieren Sie wie die Qt'ianer. Dies gilt nicht nur aus Gründen der Lesbarkeit / Konsistenz. Überlegen Sie, was passiert, wenn Sie alles in einem stl-Container speichern, dann müssen Sie all diese Daten an eine Qt-Funktion übergeben. Möchten Sie wirklich eine Menge Code verwalten, der Dinge in / aus Qt-Containern kopiert? Ihr Code ist bereits stark von Qt abhängig, daher ist es nicht so, als würden Sie ihn durch die Verwendung von stl-Containern "standardisierter" machen. Und wozu dient ein Container, wenn Sie ihn jedes Mal, wenn Sie ihn für nützliche Zwecke verwenden möchten, in den entsprechenden Qt-Container kopieren müssen?
quelle
Die Qt-Container sind begrenzter als die STL-Container. Einige Beispiele dafür, wo die STL überlegen sind (all dies habe ich in der Vergangenheit getroffen):
QList
(zeigerbasiert) undQValueList
(wertebasiert); Qt 3 hatteQPtrList
undQValueList
; Qt 4 hat jetztQList
und es ist überhaupt nichts wieQPtrList
oderQValueList
).Selbst wenn Sie am Ende die Qt-Container verwenden, verwenden Sie die STL-kompatible API-Teilmenge (dh
push_back()
nichtappend()
;front()
, nichtfirst()
, ...), um eine erneute Portierung zu vermeiden. Kommen Sie zu Qt 5. Sowohl in Qt2-> 3 als auch in Qt3-> 4 Übergänge, die Änderungen in den Qt-Containern gehörten zu denen, die die meiste Code-Abwanderung erforderten.rbegin()
/rend()
, wodurch die umgekehrte Iteration symmetrisch zur Vorwärtsiteration ist. Nicht alle Qt-Container haben sie (die assoziativen nicht), daher ist die umgekehrte Iteration unnötig kompliziert.insert()
von verschiedenen, aber kompatiblen Iteratortypen, sodass siestd::copy()
viel seltener benötigt werden.Allocator
Vorlagenargument, wodurch die benutzerdefinierte Speicherverwaltung im Vergleich zu Qt (Fork of erforderlich für ) trivial ist (typedef erforderlich ). EDIT 20171220 : Dies reduziert Qt von Fortschritten im Allokator-Design nach C ++ 11 und C ++ 17, vgl. zB John Lakos 'Vortrag ( Teil 2 ).QLineEdit
s/QString/secqstring/
std::deque
.std::list
hatsplice()
. Wann immer ich mich benutzestd::list
, liegt es daran, dass ich es brauchesplice()
.std::stack
,std::queue
Aggregieren ihre zugrunde liegenden Behälter richtig, und nicht erben sie, wieQStack
,QQueue
tun.QSet
ist wiestd::unordered_set
, nicht wiestd::set
.QList
ist einfach komisch .Viele der oben genannten Probleme könnten in Qt recht einfach gelöst werden , aber die Containerbibliothek in Qt scheint derzeit einen Mangel an Entwicklungsfokus zu haben.
EDIT 20150106 : Nachdem ich einige Zeit damit verbracht habe, C ++ 11-Unterstützung für Qt 5-Containerklassen bereitzustellen, habe ich entschieden, dass sich die Arbeit nicht lohnt. Wenn Sie sich die Arbeit ansehen, die in C ++ - Standardbibliotheksimplementierungen integriert wird, ist klar, dass die Qt-Klassen niemals aufholen werden. Wir haben Qt 5.4 jetzt freigegeben und
QVector
noch nicht Elemente auf Umschichtungen nicht bewegt, muss nichtemplace_back()
oder rvalue-push_back()
... Wir lehnten kürzlich auch eineQOptional
Klassenvorlage und wartet daraufstd::optional
statt. Ebenso fürstd::unique_ptr
. Ich hoffe, dass sich dieser Trend fortsetzt.quelle
QList
war das Äquivalent zustd::deque
. Natürlich hätte ich die Dokumentation nicht einfach überfliegen sollen.QVector
hatcrbegin
und Freunde seit Qt 5.6std::reverse_iterator
über die kaputten verwendenQHash
/QMap
Iteratoren, die, wenn sie dereferenziert, Rückkehrmapped_type
stattvalue_type
). Nichts, was nicht behoben werden kann, aber siehe meine EDIT von 2015.QVector
Verwendungenint
als sein Index, so dass 31-Bit - Größen (auch auf 64-Bit - Systeme) zu begrenzen. Darüber hinaus können nicht einmalINT_MAX
Elemente mit einer Größe von mehr als 1 Byte gespeichert werden. Zum Beispiel war das größte, das.size()
ichQVector<float>
unter x86_64 Linux gcc haben konnte, 536870907 Elemente (2²⁹-5), währendstd::vector<float>
erfolgreich 4294967295 Elemente (2³²-1 ) zugewiesen wurden. ).Zerlegen wir diese Behauptungen in tatsächlich messbare Phänomene:
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:
STL-Stil:
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
oder
C ++ 11 für jeden Stil
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:
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.
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:
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).
quelle
Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
Qts Iteratoren im Java-Stil wurden nicht zu C ++ 11 hinzugefügt; sie sind älter als sie. Aufforeach(QString elem, list)
jeden Fall ist Qt's genauso einfach wie Ceachs foreach oder BOOST_FOREACH und funktioniert mit Compilern, die vor C ++ 11 kompatibel sind.So, as we can see, this interface gains us nothing except an additional interface, *on top of* an already sleek, streamlined, and modern interface. Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
(Hervorhebung von mir) Sie haben dies direkt gesagt, nachdem Sie uns C ++ 11- und BOOST-Versionen von foreach gezeigt haben, sodass es so klingt, als ob die Qt-Version aus einer dieser beiden Versionen aufgebaut ist, was AFAICT nicht der Fall ist. Ich bin sicher, das haben Sie nicht gemeint, aber so kommt es zustande. Daher "irreführende Informationen".It's an additional layer of abstraction (and an unnecessary one) that bloats the interface, which is not easier.
Es ist immer noch unklar, womit Sie vergleichen. C ++ 03 Iteratoren? C ++ 11 Iteratoren? BOOST_FOREACH? Alles das oben Genannte?STL-Container:
quelle
Qt-Container verwenden die Copy-on-Write-Sprache.
quelle
std::basic_string
und der Standard hat mit C ++ 11 Maßnahmen ergriffen, um diese nicht konform zu machen.Eines der Hauptprobleme ist, dass die API von Qt erwartet, dass Sie Daten in den Containern von Qt bereitstellen. Sie können also auch einfach die Qt-Container verwenden, anstatt zwischen den beiden hin und her zu wechseln.
Wenn Sie die Qt-Container bereits verwenden, ist es möglicherweise etwas optimaler, sie ausschließlich zu verwenden, da Sie die STL-Header-Dateien nicht einschließen und möglicherweise in die STL-Bibliotheken verlinken müssten. Abhängig von Ihrer Toolchain kann dies jedoch trotzdem passieren. Rein aus gestalterischer Sicht ist Konsistenz im Allgemeinen eine gute Sache.
quelle
Wenn die Daten, mit denen Sie arbeiten, hauptsächlich zur Steuerung der Qt-basierten Benutzeroberfläche verwendet werden, verwenden Sie auf jeden Fall Qt-Container.
Wenn die Daten meistens intern in der App verwendet werden und Sie wahrscheinlich nie von Qt weg portieren, verwenden Sie die Qt-Container, sofern keine Leistungsprobleme auftreten, da dadurch die Datenbits, die an die Benutzeroberfläche gelangen, einfacher zu verarbeiten sind.
Wenn die Daten hauptsächlich in Verbindung mit anderen Bibliotheken verwendet werden, die nur über STL-Container Bescheid wissen, verwenden Sie STL-Container. Wenn Sie diese Situation haben, sind Sie in Schwierigkeiten, egal was passiert, weil Sie viel zwischen Containertypen hin und her portieren werden, egal was Sie tun.
quelle
Neben dem COW-Unterschied werden STL-Container auf einer Vielzahl von Plattformen viel häufiger unterstützt. Qt ist portabel genug, wenn Sie Ihre Arbeit auf "Mainstream" -Plattformen beschränken, aber die STL ist auch auf vielen anderen undurchsichtigeren Plattformen verfügbar (z. B. DSPs von Texas Instruments).
Da die STL eher Standard ist als von einem einzelnen Unternehmen kontrolliert wird, gibt es im Allgemeinen mehr Programmierer, die STL-Code leicht lesen, verstehen und ändern können, und mehr Ressourcen (Bücher, Online-Foren, Konferenzen usw.), um sie zu unterstützen tun dies als es für Qt gibt. Das heißt nicht, dass man Qt allein aus diesem Grund scheuen sollte; Wenn alle anderen Dinge gleich sind, sollten Sie standardmäßig die STL verwenden, aber natürlich sind alle Dinge selten gleich, sodass Sie in Ihrem eigenen Kontext entscheiden müssen, was am sinnvollsten ist.
In Bezug auf die Antwort von AlexKR: Die STL-Leistung ist in Grenzen garantiert, aber eine bestimmte Implementierung kann plattformabhängige Details verwenden, um ihre STL zu beschleunigen . In diesem Sinne können Sie auf verschiedenen Plattformen unterschiedliche Ergebnisse erzielen, diese sind jedoch niemals langsamer als die explizite Garantie (Modulo-Fehler).
quelle
Meine fünf Cent: Qt-Container sollen auf verschiedenen Plattformen ähnlich funktionieren. Während STL-Container von der STL-Implementierung abhängen. Möglicherweise erhalten Sie unterschiedliche Leistungsergebnisse.
EDIT: Ich sage nicht, dass STL "langsamer" ist, aber ich weise auf die Auswirkungen verschiedener Implementierungsdetails hin.
Bitte überprüfen Sie dies und dann vielleicht das .
Und es ist kein wirkliches Problem von STL. Wenn Sie einen signifikanten Leistungsunterschied haben, liegt offensichtlich ein Problem im Code vor, der STL verwendet.
quelle
Ich denke, es hängt davon ab, wie Sie Qt verwenden. Wenn Sie es überall in Ihrem Produkt verwenden, ist es wahrscheinlich sinnvoll, Qt-Behälter zu verwenden. Wenn Sie es nur zum Beispiel für den UI-Teil enthalten, ist es möglicherweise besser, C ++ - Standardcontainer zu verwenden.
quelle
Ich bin der Meinung, dass STL eine hervorragende Software ist. Wenn ich jedoch eine KDE- oder Qt-bezogene Programmierung durchführen möchte, ist Qt der richtige Weg. Es hängt auch vom verwendeten Compiler ab, mit GCC funktioniert STL ziemlich gut. Wenn Sie jedoch beispielsweise SUN Studio CC verwenden müssen, wird STL Ihnen höchstwahrscheinlich Kopfschmerzen bereiten, da der Compiler nicht die STL an sich ist. In diesem Fall, da der Compiler Ihren Kopf verletzt, verwenden Sie einfach Qt, um Ihnen die Mühe zu ersparen. Nur meine 2 Cent ...
quelle
Es gibt eine (manchmal) große Einschränkung in QVector. Es können nur int Bytes Speicher zugewiesen werden (beachten Sie, dass das Limit in Bytes und nicht in der Anzahl der Elemente liegt). Dies bedeutet, dass der Versuch, zusammenhängende Speicherblöcke mit einem QVector von mehr als ~ 2 GB zuzuweisen, zu einem Absturz führt. Dies geschieht mit Qt 4 und 5. std :: vector hat keine solche Einschränkung.
quelle
Der Hauptgrund für STL-Container ist für mich, wenn Sie einen benutzerdefinierten Allokator benötigen, um Speicher in sehr großen Containern wiederzuverwenden. Angenommen, Sie haben eine QMap, in der 1000000 Einträge (Schlüssel / Wert-Paare) gespeichert sind. In Qt bedeutet dies genau 1000000 Millionen Zuweisungen (
new
Anrufe), egal was passiert . In STL können Sie jederzeit einen benutzerdefinierten Zuweiser erstellen, der intern den gesamten Speicher auf einmal zuweist, und ihn jedem Eintrag zuweisen, wenn die Karte gefüllt wird.Mein Rat ist, STL-Container zu verwenden, wenn leistungskritische Algorithmen in die Geschäftslogik geschrieben werden, und sie dann wieder in Qt-Container zu konvertieren, wenn die Ergebnisse bereit sind, indem sie bei Bedarf von Ihren UI-Steuerelementen und Formularen angezeigt werden.
quelle
QMapNode<K,V>
für IhreK
,V
Ihr eigenes zu schaffenoperator new
.