Décomposons ces affirmations en phénomènes réels mesurables:
- Plus léger: les conteneurs Qt utilisent moins de mémoire que les conteneurs STL
- Plus sûr: les conteneurs Qt ont moins de chances d'être mal utilisés
- Plus facile: les conteneurs Qt présentent moins de charge intellectuelle
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);
QVector<int>::iterator i = a.begin();
b = a;
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();
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.