Conteneurs STL ou Qt?

Quels sont les avantages et les inconvénients de l'utilisation des conteneurs Qt ( QMap , QVector , etc.) sur leur équivalent STL?

je vois une raison de préférer Qt:

  • Qt, les conteneurs peuvent être transmis à d'autres parties de l'intervalle Qt. Par exemple, ils peuvent être utilisés pour peupler un QVariant et puis un QSettings (avec une certaine limitation cependant, seulement QList et QMap / QHash dont les clés sont des cordes sont acceptées).

y en a-t-il d'autres?

Edit : en supposant que l'application repose déjà sur Qt.

169
demandé sur Julien-L 2009-11-03 19:00:37

14 réponses

j'ai commencé par utiliser std::(w)string et les conteneurs STL exclusivement et la conversion vers/à partir des équivalents Qt, mais je suis déjà passé à QString et je trouve que j'utilise de plus en plus les conteneurs de Qt.

en ce qui concerne les chaînes, QString offre des fonctionnalités beaucoup plus complètes que std::basic_string et il est complètement unicode conscient. Il offre également une " mise en œuvre de vache efficace , sur laquelle je suis venu à compter fortement.

conteneurs de Qt:

  • offrent la même implémentation de vache que dans QString, ce qui est extrêmement utile quand il s'agit d'utiliser la macro foreach de Qt (qui fait une copie) et en utilisant des métatypes ou des signaux et des slots.
  • peut utiliser des itérateurs de type STL ou des itérateurs de type Java
  • sont diffusables avec QDataStream
  • sont largement utilisés dans L'API de Qt
  • ont une mise en œuvre stable à travers 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 controverse std::string COW). Certaines implémentations STL sont particulièrement mauvais.
  • fournir des hachures, qui ne sont pas disponibles, sauf si vous utilisez TR1

le QTL a une philosophie différente de la STL, qui est bien résumé par J. Blanchette: "attendu que les conteneurs de STL sont optimisés pour la vitesse brute, les classes de conteneurs de Qt ont été soigneusement conçues pour fournir une commodité, une utilisation minimale de la mémoire et une expansion minimale du code."

Le lien ci-dessus fournit plus de détails sur la mise en œuvre du QTL et les optimisations utilisées.

123
répondu rpg 2016-08-03 10:11:31

c'est une question difficile à répondre. Il peut vraiment se résumer à un argument philosophique/subjectif.

cela dit...

je recommande la règle " quand on est à Rome... Faites comme les Romains "

ce qui signifie que si vous êtes en terre Qt, codez comme le font les Qt'IANS. Ce n'est pas seulement pour des préoccupations de lisibilité/cohérence. Pensez à ce qui se passe si vous stockez tout dans un conteneur stl alors vous devez passez toutes ces données à une fonction Qt. Voulez-vous vraiment gérer un tas de code qui copie des choses dans/hors des conteneurs Qt. Votre code est déjà fortement dépendant de Qt, donc ce n'est pas comme si vous le faisiez plus "standard" en utilisant des conteneurs stl. Et à quoi sert un conteneur Si chaque fois que vous voulez l'utiliser pour quelque chose d'utile, vous devez le copier dans le conteneur QT correspondant?

167
répondu Doug T. 2009-11-03 16:45:59

les conteneurs Qt sont plus limités que les conteneurs STL. Quelques exemples où les STL sont supérieurs (tous ceux que j'ai touchés dans le passé):

  • STL est standardisé, ne change pas avec chaque version Qt (Qt 2 avait QList (pointeur) et QValueList (basé sur la valeur); Qt 3 avait QPtrList et QValueList ; Qt 4 a maintenant QList , et ce n'est rien du tout comme QPtrList ou QValueList ).

    Même si vous utilisez les conteneurs Qt, utilisez le sous-ensemble D'API compatible STL (i.e. push_back() , pas append() ; front() , pas first() , ...) pour éviter le portage encore une fois viennent Qt 5. Dans les transitions Qt2->3 et Qt3->4, les changements dans les conteneurs Qt étaient parmi ceux qui nécessitaient le plus de code de repliement.
  • STL bidirectionnel conteneurs ont tous rbegin() / rend() , ce qui rend l'itération inversée symétrique à l'itération avancée. Tous les conteneurs Qt n'en ont pas (les conteneurs associatifs n'en ont pas), donc l'itération inverse est inutilement compliquée.
  • conteneurs STL ont de la gamme insert() de différents mais compatibles, iterator types, faisant de std::copy() beaucoup moins souvent nécessaire.
  • conteneurs STL ont un Allocator argument de modèle, de fabrication personnalisée de gestion de la mémoire trivial (définition de type requis), par rapport à Qt (fourche de QLineEdit requis pour s/QString/secqstring/ ). EDIT 20171220 : ceci supprime Qt des avancées dans la conception des allocateurs suivant C++11 et C++17, cf. par exemple, Jean Lakos " talk ( partie 2 ).
  • il n'y a pas de Qt équivalent à std::deque .
  • std::list a splice() . Chaque fois que j'utilise std::list c'est parce que j'ai besoin de splice() .
  • std::stack , std::queue agréger correctement leur conteneur sous-jacent, et ne pas en hériter, comme QStack , QQueue fais-le.
  • QSet est comme std::unordered_set , pas comme std::set .
  • QList est un tout bizarre .

plusieurs des ci-dessus pourrait être résolu assez facilement dans Qt , mais la bibliothèque de conteneurs dans Qt semble éprouver un manque d'attention de développement à l'heure actuelle.

EDIT 20150106 : après avoir passé un certain temps à essayer D'apporter C++11-support aux classes de conteneurs Qt 5, j'ai décidé que cela ne valait pas la peine du travail. Si vous regardez le travail qui est mis dans les implémentations de bibliothèque standard C++, c'est assez clair que les cours de Qt ne rattraperont jamais. Nous avons publié Qt 5.4 maintenant et QVector " still ne déplace pas les éléments sur les réaffectations, n'a pas emplace_back() ou rvalue - push_back() ... Nous avons aussi récemment rejeté un modèle de classe QOptional , en attendant std::optional à la place. De même pour std::unique_ptr . J'espère que cette tendance continue.

58
répondu Marc Mutz - mmutz 2017-12-20 13:10:55

décomposons ces revendications en phénomènes réels mesurables:

  • plus léger: les conteneurs Qt utilisent moins de mémoire que les conteneurs STL
  • plus sûrs: les conteneurs Qt ont moins de chances d'être mal utilisés
  • plus facile: les conteneurs Qt présentent une charge intellectuelle moindre

plus facile

l'allégation formulée dans ce contexte est que l'itération de type java est en quelque sorte" plus facile " que le style STL, et donc Qt est plus facile à utiliser en raison de cette interface supplémentaire.

Java Style:

QListIterator<QString> i(list);
while (i.hasNext())
    qDebug() << i.next();

STL Style:

QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
    qDebug << *i;

le style Java iterator a l'avantage d'être un peu plus petit et plus propre. Le problème, c'est que ce n'est plus du style STL.

C++11 STL Style

for( auto i = list.begin(); i != list.end(); ++i)
    qDebug << *i;

ou

C++11 foreach style

for (QString i : list)
    qDebug << i;

qui est si radicalement simple qu'il n'y a aucune raison d'utiliser autre chose (à moins que vous ne supportiez pas C++11).

Mon préféré, cependant, est:

BOOST_FOREACH(QString i, list)
{
    qDebug << i;
}

donc, comme nous pouvons le voir, cette interface ne nous apporte rien d'autre qu'une interface supplémentaire, en plus d'une interface déjà élégante, rationalisée et moderne. Ajouter un niveau inutile d'abstraction en plus d'une déjà stable et utilisable interface? Pas mon idée de "plus facile".

de plus, les interfaces QT foreach et java ajoutent des frais généraux; elles copient la structure, et fournissent un niveau inutile d'indirection. Cela pourrait ne pas sembler beaucoup, mais pourquoi ajouter une couche de surcharge de fournir un pas-que-trop-interface plus simple? Java a cette interface parce que java n'a pas de surcharge de l'opérateur; C++ en a une.

plus sûr

la justification que Qt donne est le partage implicite problème, qui n'est ni implicite, ni un problème. Mais cela implique 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.
*/

tout d'abord, ce n'est pas implicite; vous assignez explicitement un vecteur à un autre. La spécification STL iterator 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 iterator sont suivies, absolument rien ne va mal. Le la seule fois où quelque chose tourne mal, c'est ici:

b.clear(); // Now the iterator i is completely invalid.

QT spécifie ceci comme si cela signifie quelque chose, comme si un problème surgit de novo de ce scénario. Ça ne l'est pas. L'itérateur est invalidé, et comme tout ce qui peut être accédé à partir de plusieurs zones disjointes, c'est comme ça que ça marche. En fait, cela se produira facilement avec les itérateurs de style Java dans Qt, grâce à sa forte dépendance sur le partage implicite, qui est un antimodèle comme documenté ici , et à beaucoup d'autres zones . Il semble particulièrement étrange que cette "optimisation" soit mise en œuvre dans un cadre se déplaçant de plus en plus vers le multithreading, mais c'est du marketing pour vous.

briquet

celui-ci est un peu plus compliqué. L'utilisation de Copy-on-Write et de stratégies implicites de partage et de croissance rend très difficile de réellement faire des garanties sur la quantité de mémoire que votre conteneur utilisera à tout de temps donné. C'est différent de la STL, qui vous donne de fortes garanties algorithmiques.

Nous savons le minimum lié d'espace perdu pour un vecteur est la racine carrée de la longueur du vecteur , mais il semble y avoir aucun moyen de mettre en Qt; les diverses "optimisations" ils soutiennent l'empêcherait de ce très important d'économiser de l'espace de fonctionnalité. Le LTS ne nécessite pas cette caractéristique (et la plupart utilisent un doublement de la croissance, ce qui est plus coûteux), mais il est il est important de noter que vous pouvez au moins implémenter cette fonctionnalité, si besoin est.

il en va de même pour les listes doublement liées, qui pourraient utiliser XOR pour réduire drastiquement l'espace utilisé. Encore une fois, cela est impossible avec Qt, en raison de ses besoins pour la croissance et la vache.

vache peut en effet faire quelque chose de plus léger, mais ainsi peut Intrusive conteneurs, tels que pris en charge par boost , et Qt utilisé ces fréquemment dans les versions précédentes, mais ils ne sont plus utilisés autant parce qu'ils sont difficiles à utiliser, dangereux, et imposent une charge sur le programmeur. La vache est une solution beaucoup moins intrusive, mais peu attrayante pour les raisons posé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 réellement combien de mémoire vous perdrez à un moment donné. Malheureusement, il est impossible de comparer les deux dans la mémoire brute l'utilisation, parce que de tels benchmarks montreraient des résultats très différents dans différents cas d'utilisation, ce qui est le type de problème exact que le LTS a été conçu pour corriger.

En Conclusion

éviter d'utiliser des conteneurs Qt lorsque cela est possible, sans imposer un coût de copie, et utiliser une itération de type STL (peut-être par le biais d'un wrapper ou de la nouvelle syntaxe), chaque fois que cela est possible.

24
répondu Alice 2016-03-01 23:58:02

conteneurs STL:

  • ont des garanties de bonne fin
  • peut être utilisé dans les algorithmes STL qui ont également des garanties de performance
  • peut être exploité par des bibliothèques C++ tierces comme Boost
  • sont standard, et susceptibles de survivre solutions propriétaires
  • encourager la programmation générique des algorithmes et des structures de données. Si vous écrivez nouveau algorithmes et structures de données conformes à STL vous pouvez tirer parti de ce que STL fournit déjà sans frais.
21
répondu fbrereto 2009-11-03 21:20:16

Qt conteneurs utilisation de copie sur écriture de l'idiome.

15
répondu TimW 2009-11-03 16:03:25

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 de Qt plutôt que de vous transformer en va-et-vient entre les deux.

en outre, 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 lien dans les bibliothèques STL. Cependant, selon votre chaîne d'outils, cela peut arriver de toute façon. Du point de vue de la conception, la cohérence est généralement une bonne chose.

9
répondu qid 2009-11-03 17:03:35

si les données avec lesquelles vous travaillez sont principalement utilisées pour piloter L'interface utilisateur basée sur Qt, alors utilisez certainement les conteneurs Qt.

si les données sont principalement utilisées en interne dans l'application, et que vous n'êtes jamais susceptible de vous éloigner de Qt, alors sauf en cas de problèmes de performance, 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 en conjonction avec d'autres bibliothèques qui ne connaissent que STL des conteneurs, puis utiliser des conteneurs STL. Si vous rencontrez cette situation, vous êtes en difficulté n'importe quoi parce que vous allez faire beaucoup de portage à l'arrière-et-vient entre les types de conteneurs, peu importe ce que vous faites.

8
répondu Michael Kohne 2009-11-03 21:29:09

outre la différence de vache, les conteneurs STL sont beaucoup plus largement supportés sur une variété de plateformes. Qt est suffisamment portable si vous limitez votre travail aux plates-formes "mainstream", mais le STL est également disponible sur de nombreuses autres plates-formes plus obscures (par exemple, les DSPs de Texas Instruments).

parce que la STL est standard plutôt que contrôlée par une seule société, il y a, en général, plus de programmeurs qui peuvent facilement lire, comprendre et modifier le code STL et plus ressources (livres, forums en ligne, conférences, etc.)) pour les aider à le faire qu'il n'y en a pour Qt. Cela ne veut pas dire que L'on devrait se détourner de Qt pour cette seule raison; juste que, toutes les autres choses étant égales, vous devriez par défaut à la STL, mais bien sûr toutes les choses sont rarement égales, donc vous devrez décider dans votre propre contexte qui est le plus sensé.

en ce qui concerne la réponse D'AlexKR: la performance STL est garantie dans les limites, mais une donnée la mise en œuvre peut faire usage des détails dépendant 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 plateformes, mais il ne sera jamais plus lent que la garantie explicite (bogues modulo).

7
répondu metal 2009-11-03 16:29:43

mes cinq cents: Les conteneurs Qt sont censés fonctionner de la même façon sur différentes plateformes. Tandis que les conteneurs STL dépendent de L'implémentation STL. Vous pourriez 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 de mise en œuvre.

S'il vous plaît cochez cette , et puis peut-être cette .

Et ce n'est pas un vrai problème de LST. Évidemment, si vous avez une différence significative dans les performances, alors il y a un problème dans le code qui utilise STL.

3
répondu alexkr 2017-05-23 12:26:18

je suppose que cela dépend de la façon dont vous utilisez Qt. Si vous l'utilisez sur votre produit, qu'il est probablement judicieux d'utiliser Qt conteneurs. Si vous le contenez seulement (par exemple) à la portion UI, il peut être préférable d'utiliser des conteneurs standard C++.

3
répondu Nemanja Trifunovic 2009-11-03 21:14:46

je suis d'avis que STL est un excellent logiciel, mais si je dois faire de la programmation liée à KDE ou Qt, alors Qt est la voie à suivre. De plus, cela dépend du compilateur que vous utilisez, avec GCC STL fonctionne assez bien mais si vous devez utiliser say Sun Studio CC puis STL vous apportera très probablement des maux de tête en raison du compilateur pas le STL en soi. Dans ce cas, puisque le compilateur va vous faire mal à la tête il suffit d'utiliser Qt pour vous épargner la peine. Juste mes 2 cents...

3
répondu Paulo Lopes 2009-11-03 21:25:00

il y a une (parfois) grande limitation dans QVector. il ne peut attribuer 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 de plus de ~2 Go avec un QVector conduira à un crash. Cela se produit avec les Qt 4 et 5. std:: vecteur n'a pas une telle limitation.

3
répondu fedemp 2014-05-25 13:35:24

la raison principale pour aller avec 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 grands 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'assigner à chaque entrée que la carte est peuplée.

mon conseil est d'utiliser des conteneurs STL lors de l'écriture d'algorithmes critiques de performance dans la logique de l'entreprise, puis les convertir de nouveau en conteneurs Qt lorsque les résultats sont prêts à être affichés par vos contrôles D'interface utilisateur et des formulaires si nécessaire.

0
répondu Darien Pardinas 2014-12-20 22:32:08