J'ai commencé par utiliser std::(w)string
et les conteneurs STL exclusivement et en convertissant vers / depuis les équivalents Qt, mais je suis déjà passé QString
et je trouve que j'utilise de plus en plus les conteneurs Qt.
En ce qui concerne les chaînes, QString
offre des fonctionnalités beaucoup plus complètes par rapport à std::basic_string
et il est entièrement compatible Unicode. Il offre également une implémentation efficace de COW , sur laquelle je me fie beaucoup.
Les conteneurs de Qt:
- offrent la même implémentation COW que dans
QString
, ce qui est extrêmement utile lorsqu'il s'agit d'utiliser la foreach
macro de Qt (qui fait une copie) et lors de l'utilisation de méta-types ou de signaux et de slots.
- peut utiliser des itérateurs de style STL ou des itérateurs de style Java
- sont diffusables avec
QDataStream
- sont largement utilisés dans l'API de Qt
- ont une implémentation stable sur tous les systèmes d'exploitation. Une implémentation STL doit obéir au standard C ++, mais est par ailleurs libre de faire ce qu'il veut (voir la
std::string
controverse COW). Certaines implémentations STL sont particulièrement mauvaises.
- fournir des hachages, qui ne sont disponibles que si vous utilisez TR1
Le QTL a une philosophie différente de la STL, qui est bien résumée par J. Blanchette: "Alors que les conteneurs de STL sont optimisés pour la vitesse brute, les classes de conteneurs de Qt ont été soigneusement conçues pour offrir une commodité, une utilisation minimale de la mémoire et une expansion de code minimale."
Le lien ci-dessus fournit plus de détails sur l'implémentation du QTL et les optimisations utilisées.
QList<double>
sur une architecture 32 bits pour une utilisation de la mémoire pour voir par vous-même.QVector
au lieu deQList
. Il y a une jolie explication Qt, que QList est conçu pour stocker des pointeurs sur des objets. Ainsi, chaque élément double créé dynamiquement et le pointeur vers cet élément est stockéQList
. QList est conçu comme un conteneur «intermédiaire» entre le vecteur et la liste chaînée. Il n'est pas conçu pour les cas critiques de mémoire / performances.C'est une question à laquelle il est difficile de répondre. Cela peut vraiment se résumer à un argument philosophique / subjectif.
Cela étant dit...
Je recommande la règle "Quand à Rome ... fais comme les Romains"
Ce qui signifie que si vous êtes sur le terrain Qt, codez comme le font les Qt'ians. Ce n'est pas seulement pour des problèmes de lisibilité / cohérence. Considérez ce qui se passe si vous stockez tout dans un conteneur stl, vous devez alors transmettre toutes ces données à une fonction Qt. Voulez-vous vraiment gérer un tas de code qui copie des éléments dans / hors des conteneurs Qt. Votre code dépend déjà fortement de Qt, donc ce n'est pas comme si vous le rendiez plus «standard» en utilisant des conteneurs stl. Et quel est l'intérêt d'un conteneur si à chaque fois que vous souhaitez l'utiliser pour quelque chose d'utile, vous devez le copier dans le conteneur Qt correspondant?
la source
Les conteneurs Qt sont plus limités que ceux de la STL. Quelques exemples où les STL sont supérieurs (tous ceux que j'ai rencontrés dans le passé):
QList
(basé sur un pointeur) etQValueList
(basé sur une valeur); Qt 3 avaitQPtrList
etQValueList
; Qt 4 a maintenantQList
, et ce n'est rien du tout commeQPtrList
ouQValueList
). Qt 6 aura unQList
qui estQVector
toutQVector
sera dépréciée . (. -À- dire même si vous finissez par utiliser les conteneurs Qt, utilisez le sous - ensemble de l' API STL compatiblepush_back()
, nonappend()
,front()
nonfirst()
, ...) afin d' éviter le portage viennent encore une fois Qt 6. Dans les deux Qt2-> 3 et Qt3-> 4 transitions, les changements dans les conteneurs Qt étaient parmi ceux nécessitant le plus de désabonnement de code. J'attends la même chose pour Qt5-> 6.rbegin()
/rend()
, ce qui rend l'itération inverse symétrique à l'itération directe. Tous les conteneurs Qt n'en ont pas (les associatifs n'en ont pas), donc l'itération inverse est inutilement compliquée.insert()
de types d'itérateurs différents, mais compatibles, ce qui les rendstd::copy()
beaucoup moins souvent nécessaires.Allocator
argument de modèle, ce qui rend la gestion de la mémoire personnalisée triviale (typedef requis), par rapport à Qt (fork deQLineEdit
requis pours/QString/secqstring/
). EDIT 20171220 : Cela coupe Qt des avancées dans la conception d'allocateurs suivant C ++ 11 et C ++ 17, cf. Par exemple, le discours de John Lakos ( partie 2 ).std::deque
.std::list
asplice()
. Chaque fois que je me retrouve à utiliserstd::list
, c'est parce que j'ai besoinsplice()
.std::stack
,std::queue
Agréger correctement leur contenant sous - jacent, et ne pas hériter, commeQStack
,QQueue
faire.QSet
est commestd::unordered_set
, pas commestd::set
.QList
est juste bizarre .Beaucoup de ce qui précède pourrait être résolu assez facilement dans Qt , mais la bibliothèque de conteneurs de Qt semble éprouver un manque de concentration sur le développement pour le moment.
EDIT 20150106 : Après avoir passé un certain temps à essayer d'apporter le support C ++ 11 aux classes de conteneurs Qt 5, j'ai décidé que cela ne valait pas le travail. Si vous regardez le travail qui est mis en œuvre dans les implémentations de bibliothèques standard C ++, il est tout à fait clair que les classes Qt ne rattraperont jamais. Nous avons publié Qt 5.4 maintenant etne déplace
QVector
toujours pas les éléments sur les réallocations, n'a pasemplace_back()
ou rvalue-push_back()
... Nous avons également récemment rejeté unQOptional
modèle de classe, en attendant à lastd::optional
place. De même pourstd::unique_ptr
. J'espère que cette tendance se poursuivra.MODIFIER 20201009 : Venez Qt 6, ils nouveau leurs conteneurs de manière incompatible:
QVector
sera renomméQList
, vous perdez donc la stabilité de la référence lors de l'utilisationQList
.QVector
(le nom) sera obsolète .QLinkedList
sera supprimé.QHash
etQSet
sont maintenant des tables de hachage à adressage ouvert, perdant également des garanties de stabilité de référenceQMap
sera soutenu parstd::map
, éventuellement en changeant le comportement d'insertion et, pourQMultiMap
, l'ordre des éléments équivalents.qsizetype
(plus ou moinsstd::ptrdiff_t
) (était:)int
.Donc, si vous voulez réécrire votre code utilisant des conteneurs, continuez avec les conteneurs Qt. Tout le monde bénéficie de décennies de stabilité avec les conteneurs STL.
la source
QList
c'était l'équivalent destd::deque
. De toute évidence, je n'aurais pas dû simplement parcourir la documentation.QVector
a eu descrbegin
amis depuis Qt 5.6std::reverse_iterator
lesQHash
/QMap
itérateurs cassés , qui, une fois déréférencés, reviennent à lamapped_type
place devalue_type
). Rien qui ne peut être corrigé, mais voir mon EDIT de 2015.QVector
utiliseint
comme index, limitant ainsi les tailles de 31 bits (même sur les systèmes 64 bits). De plus, il ne peut même pas stocker desINT_MAX
éléments de taille supérieure à 1 octet. Par exemple, le plus grand que.size()
je pouvais avoirQVector<float>
sur gcc Linux x86_64 était de 536870907 éléments (2²⁹-5), tout en allouantstd::vector<float>
avec succès 4294967295 éléments (2³²-1; je n'ai pas essayé plus en raison du manque de RAM pour cela (cette taille prend déjà 16 Gio) ).Décomposons ces affirmations en phénomènes réels mesurables:
Plus facile
La revendication faite dans ce contexte est que l'itération de style Java est en quelque sorte "plus facile" que le style STL, et donc Qt est plus facile à utiliser grâce à cette interface supplémentaire.
Style Java:
QListIterator<QString> i(list); while (i.hasNext()) qDebug() << i.next();
Style STL:
QList<QString>::iterator i; for (i = list.begin(); i != list.end(); ++i) qDebug << *i;
Le style d'itérateur Java a l'avantage d'être un peu plus petit et plus propre. Le problème est que ce n'est plus du style STL.
Style C ++ 11 STL
for( auto i = list.begin(); i != list.end(); ++i) qDebug << *i;
ou
C ++ 11 pour chaque style
for (QString i : list) qDebug << i;
Ce qui est tellement simple qu'il n'y a aucune raison d'utiliser quoi que ce soit d'autre (à moins que vous ne supportiez pas C ++ 11).
Mon préféré, cependant, est:
BOOST_FOREACH(QString i, list) { qDebug << i; }
Donc, comme on peut le voir, cette interface ne nous rapporte rien sauf une interface supplémentaire, en plus d'une interface déjà épurée, rationalisée et moderne. Ajouter un niveau d'abstraction inutile en plus d'une interface déjà stable et utilisable? Pas mon idée de «plus facile».
De plus, les interfaces Qt foreach et java ajoutent une surcharge; ils copient la structure et fournissent un niveau d'indirection inutile. Cela peut ne pas sembler beaucoup, mais pourquoi ajouter une couche de surcharge pour fournir une interface pas si simple? Java a cette interface car java n'a pas de surcharge d'opérateurs; C ++ le fait.
Plus sûr
La justification que Qt donne est le problème du partage implicite, qui n'est ni implicite ni un problème. Cela implique cependant un partage.
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. */
Premièrement, ce n'est pas implicite; vous attribuez explicitement un vecteur à un autre. La spécification de l'itérateur STL indique clairement que les itérateurs appartiennent au conteneur, nous avons donc clairement introduit un conteneur partagé entre b et a. Deuxièmement, ce n'est pas un problème; tant que toutes les règles de la spécification de l'itérateur sont respectées, absolument rien ne va mal. Le seul moment où quelque chose ne va pas, c'est ici:
b.clear(); // Now the iterator i is completely invalid.
Qt spécifie cela comme si cela signifiait quelque chose, comme un problème surgit de novo de ce scénario. Ce n'est pas le cas. L'itérateur est invalidé, et comme tout ce qui est accessible à partir de plusieurs zones disjointes, c'est ainsi que cela fonctionne. En fait, cela se produira facilement avec les itérateurs de style Java dans Qt, grâce à sa forte dépendance au partage implicite, qui est un anti-modèle comme documenté ici , et dans de nombreux autres domaines . Il semble particulièrement étrange que cette «optimisation» soit mise en œuvre dans un cadre évoluant de plus en plus vers le multithreading, mais c'est le marketing pour vous.
Plus léger
Celui-ci est un peu plus délicat. L'utilisation de stratégies de copie sur écriture et de partage implicite et de croissance rend très difficile la garantie de la quantité de mémoire utilisée par votre conteneur à un moment donné. Ceci est différent de la STL, qui vous donne de solides garanties algorithmiques.
Nous savons que la limite minimale de l'espace perdu pour un vecteur est la racine carrée de la longueur du vecteur , mais il ne semble y avoir aucun moyen d'implémenter cela dans Qt; les diverses "optimisations" prises en charge excluraient cette fonction très importante d'économie d'espace. La STL ne nécessite pas cette fonctionnalité (et la plupart utilisent une croissance doublée, ce qui représente plus de gaspillage), mais il est important de noter que vous pouvez au moins implémenter cette fonctionnalité, si nécessaire.
Il en va de même pour les listes à double chaînage, qui pourraient utiliser la liaison XOr pour réduire considérablement l'espace utilisé. Encore une fois, cela est impossible avec Qt, en raison de ses exigences de croissance et de COW.
COW peut en effet faire quelque chose de plus léger, mais les conteneurs intrusifs, tels que pris en charge par boost , et Qt les utilisaient fréquemment dans les versions précédentes, mais ils ne sont plus autant utilisés car ils sont difficiles à utiliser, dangereux et imposent un fardeau. sur le programmeur. COW est une solution beaucoup moins intrusive, mais peu attractive pour les raisons évoquées ci-dessus.
Il n'y a aucune raison pour que vous ne puissiez pas utiliser des conteneurs STL avec le même coût de mémoire ou moins que les conteneurs de Qt, avec l'avantage supplémentaire de savoir combien de mémoire vous gaspillerez à un moment donné. Il est malheureusement impossible de comparer les deux dans l'utilisation de la mémoire brute, car de tels benchmarks montreraient des résultats extrêmement différents dans différents cas d'utilisation, ce qui est le type exact de problème que la STL a été conçue pour corriger.
En conclusion
Évitez d'utiliser les conteneurs Qt lorsque cela est possible pour le faire sans imposer de coût de copie, et utilisez l'itération de type STL (peut-être via un wrapper ou la nouvelle syntaxe), dans la mesure du possible.
la source
Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
les itérateurs de style Java de Qt n'ont pas été ajoutés à C ++ 11; ils sont antérieurs. Quoi qu'il en soit, Qtforeach(QString elem, list)
est aussi simple que foreach ou BOOST_FOREACH de C ++ 11 et il fonctionne avec des compilateurs compatibles pré-C ++ 11.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".
(c'est moi qui souligne) Vous avez dit cela juste après nous avoir montré les versions C ++ 11 et BOOST de foreach, ce qui donne l'impression que la version Qt est construite à partir de l'un de ces deux, ce qui n'est pas le cas d'AFAICT. Je suis sûr que ce n'est pas ce que vous vouliez dire, mais c'est ainsi que ça se passe. D'où «informations trompeuses».Conteneurs STL:
la source
Les conteneurs Qt utilisent un idiome de copie sur écriture.
la source
std::basic_string
et le standard a pris des mesures avec C ++ 11 pour rendre ce non-conforme.L'un des principaux problèmes est que l'API de Qt s'attend à ce que vous fournissiez des données dans les conteneurs de Qt, vous pouvez donc tout simplement utiliser les conteneurs Qt plutôt que de faire des allers-retours entre les deux.
De plus, si vous utilisez déjà les conteneurs Qt, il pourrait être légèrement plus optimal de les utiliser exclusivement, car vous n'auriez pas à inclure les fichiers d'en-tête STL et éventuellement à créer un lien dans les bibliothèques STL. Cependant, en fonction de votre chaîne d'outils, cela peut arriver de toute façon. Du point de vue du design, la cohérence est généralement une bonne chose.
la source
Si les données avec lesquelles vous travaillez sont principalement utilisées pour piloter l'interface utilisateur basée sur Qt, utilisez définitivement les conteneurs Qt.
Si les données sont principalement utilisées en interne dans l'application et que vous ne risquez jamais de vous éloigner de Qt, à l'exception des problèmes de performances, utilisez les conteneurs Qt, car cela rendra les bits de données qui vont à l'interface utilisateur plus faciles à traiter.
Si les données sont principalement utilisées avec d'autres bibliothèques qui ne connaissent que les conteneurs STL, utilisez des conteneurs STL. Si vous rencontrez cette situation, vous avez des problèmes quoi qu'il arrive, car vous allez faire beaucoup de transferts entre les types de conteneurs, peu importe ce que vous faites.
la source
Outre la différence COW, les conteneurs STL sont beaucoup plus largement pris en charge sur une variété de plates-formes. Qt est suffisamment portable si vous limitez votre travail aux plates-formes "grand public", mais la STL est également disponible sur de nombreuses autres plates-formes plus obscures (par exemple, les DSP de Texas Instruments).
Parce que la STL est standard plutôt que contrôlée par une seule société, il y a, d'une manière générale, plus de programmeurs qui peuvent facilement lire, comprendre et modifier le code STL et plus de ressources (livres, forums en ligne, conférences, etc.) pour les soutenir dans faire cela qu'il n'y en a pour Qt. Cela ne veut pas dire que l'on devrait éviter Qt pour cette seule raison; juste cela, toutes choses étant égales par ailleurs, vous devriez utiliser par défaut la STL, mais bien sûr toutes choses sont rarement égales, vous devrez donc décider dans votre propre contexte ce qui a le plus de sens.
En ce qui concerne la réponse d'AlexKR: les performances de la STL sont garanties dans certaines limites, mais une implémentation donnée peut utiliser des détails dépendants de la plate-forme pour accélérer leur STL. Donc, dans ce sens, vous pouvez obtenir des résultats différents sur différentes plates-formes, mais cela ne sera jamais plus lent que la garantie explicite (bogues modulo).
la source
Mes cinq cents: les conteneurs Qt sont censés fonctionner de la même manière sur différentes plates-formes. Alors que les conteneurs STL dépendent de l'implémentation STL. Vous pouvez obtenir des résultats de performances différents.
EDIT: Je ne dis pas que STL est "plus lent" mais je souligne les effets de divers détails d'implémentation.
Veuillez vérifier ceci , puis peut - être ceci .
Et ce n'est pas un vrai problème de STL. Évidemment, si vous avez une différence de performances significative, il y a un problème dans le code qui utilise STL.
la source
Je suppose que cela dépend de la façon dont vous utilisez Qt. Si vous l'utilisez partout dans votre produit, il est probablement logique d'utiliser des conteneurs Qt. Si vous le contenez uniquement (par exemple) dans la partie de l'interface utilisateur, il peut être préférable d'utiliser des conteneurs standard C ++.
la source
Je suis d'avis que STL est un excellent logiciel, mais si je dois faire de la programmation liée à KDE ou à Qt, Qt est la voie à suivre. Cela dépend également du compilateur que vous utilisez, avec GCC STL fonctionne plutôt bien, mais si vous devez utiliser SUN Studio CC, STL vous causera probablement des maux de tête à cause du compilateur et non du STL en soi. Dans ce cas, puisque le compilateur vous fera mal à la tête, utilisez simplement Qt pour vous éviter les ennuis. Juste mes 2 cents ...
la source
Il y a (parfois) une grande limitation dans QVector. Il ne peut allouer que des octets int de mémoire (notez que la limite est en octets et non en nombre d'éléments). Cela implique qu'essayer d'allouer des blocs contigus de mémoire plus grands que ~ 2 Go avec un QVector entraînera un crash. Cela se produit avec Qt 4 et 5. std :: vector n'a pas une telle limitation.
la source
La principale raison d'utiliser les conteneurs STL pour moi est si vous avez besoin d'un allocateur personnalisé afin de réutiliser la mémoire dans de très gros conteneurs. Supposons par exemple que vous ayez un QMap qui stocke 1000000 entrées (paires clé / valeur). Dans Qt, cela implique exactement 1000000 millions d'allocations (
new
appels) quoi qu'il arrive. Dans STL, vous pouvez toujours créer un allocateur personnalisé qui alloue en interne toute cette mémoire à la fois et l'affecte à chaque entrée au fur et à mesure que la carte est remplie.Mon conseil est d'utiliser des conteneurs STL lors de l'écriture d'algorithmes critiques de performances dans la logique métier, puis de les reconvertir en conteneurs Qt lorsque les résultats sont prêts à être affichés par vos contrôles et formulaires d'interface utilisateur si nécessaire.
la source
QMapNode<K,V>
pour le vôtreK
,V
pour fournir le vôtreoperator new
.