Dividamos estas afirmaciones en fenómenos mensurables reales:
- Más ligero: los contenedores Qt usan menos memoria que los contenedores STL
- Más seguro: los contenedores Qt tienen menos oportunidades de ser utilizados incorrectamente
- Más fácil: los contenedores Qt presentan menos carga intelectual
Más fácil
La afirmación hecha en este contexto es que la iteración de estilo java es de alguna manera "más fácil" que el estilo STL y, por lo tanto, Qt es más fácil de usar debido a esta interfaz adicional.
Estilo Java
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
Estilo STL:
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
qDebug << *i;
El estilo de iterador de Java tiene la ventaja de ser un poco más pequeño y limpio. El problema es que esto ya no es en realidad estilo STL.
Estilo C ++ 11 STL
for( auto i = list.begin(); i != list.end(); ++i)
qDebug << *i;
o
C ++ 11 estilo foreach
for (QString i : list)
qDebug << i;
Lo cual es tan drásticamente simple que no hay razón para usar otra cosa (a menos que no sea compatible con C ++ 11).
Mi favorito, sin embargo, es:
BOOST_FOREACH(QString i, list)
{
qDebug << i;
}
Entonces, como podemos ver, esta interfaz no nos aporta nada más que una interfaz adicional, además de una interfaz ya elegante, optimizada y moderna. ¿Agregar un nivel innecesario de abstracción sobre una interfaz ya estable y utilizable? No es mi idea de "más fácil".
Además, las interfaces Qt foreach y java agregan sobrecarga; copian la estructura y proporcionan un nivel innecesario de indirección. Puede que esto no parezca mucho, pero ¿por qué agregar una capa de sobrecarga para proporcionar una interfaz no tan simple? Java tiene esta interfaz porque Java no tiene sobrecarga del operador; C ++ hace.
Más seguro
La justificación que da Qt es el problema de compartir implícitamente, que no es ni implícito ni un problema. Sin embargo, implica compartir.
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.
*/
Primero, esto no es implícito; Usted está asignando explícitamente un vector a otro. La especificación del iterador STL indica claramente que los iteradores pertenecen al contenedor, por lo que claramente hemos introducido un contenedor compartido entre by a. Segundo, esto no es un problema; siempre y cuando se sigan todas las reglas de la especificación del iterador, absolutamente nada saldrá mal. La única vez que algo sale mal es aquí:
b.clear(); // Now the iterator i is completely invalid.
Qt especifica esto como si significara algo, como un problema surge de novo de este escenario. No lo hace. El iterador está invalidado, y al igual que cualquier cosa a la que se pueda acceder desde múltiples áreas disjuntas, así es como funciona. De hecho, esto ocurrirá fácilmente con los iteradores de estilo Java en Qt, gracias a su gran dependencia del intercambio implícito, que es un antipatrón como se documenta aquí , y en muchas otras áreas . Parece especialmente extraño que esta "optimización" se ponga en uso en un marco que se mueve cada vez más hacia el subprocesamiento múltiple, pero eso es marketing para usted.
Encendedor
Este es un poco más complicado. El uso de estrategias de copia y escritura y uso compartido implícito y crecimiento hace que sea muy difícil garantizar la cantidad de memoria que usará su contenedor en un momento dado. Esto es diferente al STL, que le brinda fuertes garantías algorítmicas.
Sabemos que el límite mínimo de espacio desperdiciado para un vector es la raíz cuadrada de la longitud del vector , pero parece que no hay forma de implementar esto en Qt; las diversas "optimizaciones" que admiten impedirían esta característica tan importante de ahorro de espacio. El STL no requiere esta característica (y la mayoría usa un crecimiento doble, que es más derrochador), pero es importante tener en cuenta que al menos podría implementar esta característica, si es necesario.
Lo mismo se aplica a las listas doblemente enlazadas, que podrían usar enlaces XOr para reducir drásticamente el espacio utilizado. Nuevamente, esto es imposible con Qt, debido a sus requisitos de crecimiento y VACA.
De hecho, COW puede hacer algo más liviano, pero también lo pueden hacer los Intrusive Containers, como los respaldados por boost , y Qt los usó con frecuencia en las versiones anteriores, pero ya no se usan tanto porque son difíciles de usar, inseguros e imponen una carga en el programador. COW es una solución mucho menos intrusiva, pero poco atractiva por las razones planteadas anteriormente.
No hay ninguna razón por la que no pueda usar contenedores STL con el mismo costo de memoria o menos que los contenedores de Qt, con el beneficio adicional de saber realmente cuánta memoria perderá en un momento dado. Desafortunadamente, es imposible comparar los dos en el uso de la memoria sin procesar, porque tales puntos de referencia mostrarían resultados muy diferentes en diferentes casos de uso, que es el tipo exacto de problema que el STL fue diseñado para corregir.
En conclusión
Evite el uso de Qt Containers cuando sea posible hacerlo sin imponer un costo de copia, y use la iteración de tipo STL (tal vez a través de un contenedor o la nueva sintaxis), siempre que sea posible.