[c++] ¿Contenedores STL o Qt?



Answers

Esta es una pregunta difícil de responder. Realmente puede reducirse a un argumento filosófico / subjetivo.

Habiendo dicho eso...

Recomiendo la regla "Cuando estés en Roma ... Haz lo que hacen los romanos"

Lo que significa que si estás en Qt land, codifica como lo hacen los Qt'ians. Esto no es solo para preocupaciones de legibilidad / coherencia. Considere lo que sucede si almacena todo en un contenedor stl, entonces tiene que pasar toda esa información a una función Qt. ¿De verdad quieres administrar un montón de código que copia cosas dentro y fuera de los contenedores de Qt? Su código ya depende mucho de Qt, por lo que no es como si estuviera convirtiéndolo en un "estándar" más mediante el uso de contenedores STL. ¿Y de qué sirve un contenedor si cada vez que desea utilizarlo para algo útil, debe copiarlo en el contenedor Qt correspondiente?

Question

¿Cuáles son los pros y los contras del uso de contenedores Qt ( QMap , QVector , etc.) sobre su equivalente STL?

Puedo ver una razón para preferir Qt:

  • Los contenedores Qt se pueden pasar a otras partes de Qt. Por ejemplo, se pueden usar para llenar un QVariant y luego un QSettings (con algunas limitaciones, sin embargo, solo se QList y QMap / QHash cuyas claves son cadenas).

Hay alguna otra?

Editar : Suponiendo que la aplicación ya se basa en Qt.




Vamos a desglosar estas afirmaciones en fenómenos medibles reales:

  • Encendedor: los contenedores Qt usan menos memoria que los contenedores STL
  • Más seguro: los contenedores Qt tienen menos oportunidades de ser utilizados de forma incorrecta
  • Más fácil: los contenedores Qt presentan una carga intelectual menor

Más fácil

La afirmación hecha en este contexto es que la iteración 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 de 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 del iterador de Java tiene la ventaja de ser un poco más pequeño y más limpio. El problema es que este ya no es el estilo STL.

C ++ 11 STL Style

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 algo más (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 gana nada, excepto una interfaz adicional, además de una interfaz ya elegante, funcional 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 demasiado, pero ¿por qué agregar una capa de sobrecarga para proporcionar una interfaz que no sea mucho más sencilla? Java tiene esta interfaz porque Java no tiene sobrecarga del operador; C ++ lo hace.

Más seguro

La justificación que da Qt es el problema de compartir implícito, que no es implícito ni es 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; estás asignando explícitamente un vector a otro. La especificación del iterador STL indica claramente que los iteradores pertenecen al contenedor, por lo que hemos introducido claramente un contenedor compartido entre by a. Segundo, esto no es un problema; siempre que se sigan todas las reglas de la especificación del iterador, absolutamente nada saldrá mal. La única vez que algo sale mal está aquí:

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

Qt especifica esto como si significa algo, como si un problema surgiera de novo de este escenario. No es así El iterador está invalidado, y al igual que cualquier elemento al 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 uso compartido implícito, que es un antipatrón como se documenta here , y en muchas otras areas . Parece especialmente extraño que esta "optimización" se utilice en un marco que se mueve cada vez más hacia el multihilo, pero eso es marketing para usted.

Encendedor

Este es un poco más complicado. El uso de Copy-On-Write e Implicit Sharing and Growth Strategies hace que sea muy difícil garantizar realmente la cantidad de memoria que usará su contenedor en un momento dado. Esto es diferente al STL, que le otorga fuertes garantías algorítmicas.

Sabemos que el límite mínimo del 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 excluirían esta importante función de ahorro de espacio. El STL no requiere esta característica (y la mayoría usa un crecimiento duplicado, lo que es más inútil), pero es importante tener en cuenta que al menos podría implementar esta función, si fuera necesario.

Lo mismo puede decirse de las listas doblemente vinculadas, que podrían usar enlaces XOr para reducir drásticamente el espacio utilizado. De nuevo, esto es imposible con Qt, debido a sus requisitos de crecimiento y COW.

De hecho, COW puede hacer algo más liviano, pero también los Intrusive Containers, como los compatibles con boost , y Qt los usaron con frecuencia en las versiones anteriores, pero ya no se usan porque son difíciles de usar, inseguros e imponen una carga en el programador. VACA es una solución mucho menos intrusiva, pero poco atractiva por las razones planteadas anteriormente.

No hay ninguna razón por la cual no pueda usar contenedores STL con el mismo costo de memoria o menos que los contenedores de Qt, con el beneficio adicional de saber cuánta memoria perderá en un momento dado. Desafortunadamente, es imposible comparar los dos en el uso de la memoria bruta, ya que estos 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 siempre que sea posible hacerlo sin imponer un costo de copia, y utilice la iteración de tipo STL (quizás a través de un contenedor o la nueva sintaxis), siempre que sea posible.




La razón principal para ir con contenedores STL para mí es si necesita un asignador personalizado para reutilizar la memoria en contenedores muy grandes. Supongamos, por ejemplo, que tiene un QMap que almacena 1000000 entradas (pares clave / valor). En Qt eso implica exactamente 1000000 millones de asignaciones (llamadas new ) sin importar qué. En STL siempre puede crear un asignador personalizado que internamente asigna toda esa memoria a la vez y asignarla a cada entrada a medida que se llena el mapa.

Mi consejo es usar contenedores STL al escribir algoritmos críticos para el rendimiento en la lógica empresarial y luego convertirlos a contenedores Qt cuando los resultados estén listos para mostrarlos en los controles y formularios de la interfaz de usuario si es necesario.




Los contenedores Qt usan modismos de copiar y escribir.




Si los datos con los que está trabajando se utilizan principalmente para conducir la interfaz de usuario basada en Qt, entonces definitivamente use contenedores Qt.

Si la mayoría de los datos se usan internamente en la aplicación, y es probable que nunca se aleje de Qt, entonces, salvo problemas de rendimiento, use los contenedores Qt, ya que hará que los bits de datos que van a la interfaz de usuario sean más fáciles de manejar.

Si los datos se utilizan principalmente junto con otras bibliotecas que solo conocen contenedores STL, entonces use contenedores STL. Si tienes esta situación, estás en problemas sin importar qué cosa, porque vas a hacer muchos cambios de ida y vuelta entre los tipos de contenedores sin importar lo que hagas.




Mis cinco centavos: se supone que los contenedores Qt funcionan de manera similar en diferentes plataformas. Mientras que los contenedores STL dependen de la implementación de STL. Puede obtener diferentes resultados de rendimiento.

EDITAR: No estoy diciendo que STL sea "más lento", pero apunto los efectos de varios detalles de implementación.
Por favor revisa this , y luego tal vez this .
Y no es un problema real de STL. Obviamente, si tiene una diferencia significativa en el rendimiento, entonces hay un problema en el código que usa STL.




Soy de la opinión de que STL es una excelente pieza de software, sin embargo, si tengo que hacer algo de programación relacionada con KDE o Qt, entonces Qt es el camino a seguir. También depende del compilador que está utilizando, con GCC STL funciona bastante bien; sin embargo, si tiene que usar decir SUN Studio CC entonces STL probablemente le traerá dolores de cabeza debido al compilador, no al STL per se. En ese caso, dado que el compilador hará que te duela la cabeza, utiliza Qt para ahorrarte el problema. Solo mis 2 centavos ...






Related



Tags

c++ c++   qt qt   stl