verificar usar qué pueden para métodos libreria hay elementos contenedor clase c++ qt stl

c++ - usar - ¿Contenedores STL o Qt?



clase stl c++ (14)

¿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.


Además de la diferencia de COW, los contenedores STL son mucho más compatibles con una variedad de plataformas. Qt es lo suficientemente portátil si limita su trabajo a plataformas "convencionales", pero el STL también está disponible en muchas otras plataformas más oscuras (por ejemplo, los DSP de Texas Instruments).

Debido a que el STL es estándar en lugar de estar controlado por una sola corporación, en general hay más programadores que pueden leer, comprender y modificar fácilmente el código STL y más recursos (libros, foros en línea, conferencias, etc.) para apoyarlos en haciendo esto que hay para Qt. Eso no quiere decir que uno deba alejarse de Qt solo por esta razón; solo que, en igualdad de condiciones, debe usar el STL por defecto, pero por supuesto todas las cosas rara vez son iguales, por lo que tendrá que decidir en su propio contexto lo que tenga más sentido.

Con respecto a la respuesta de AlexKR: el rendimiento de STL está garantizado dentro de los límites, pero una implementación determinada puede hacer uso de detalles dependientes de la plataforma para acelerar su STL. Entonces, en ese sentido, puede obtener resultados diferentes en diferentes plataformas, pero nunca será más lento que la garantía explícita (errores de módulo).


Comencé usando std: :( ​​w) string y los contenedores STL exclusivamente y convirtiendo a / desde los equivalentes de Qt, pero ya he cambiado a QString y encuentro que estoy usando los contenedores de Qt cada vez más.

Cuando se trata de cadenas, QString ofrece una funcionalidad mucho más completa en comparación con std :: basic_string y es completamente consciente del Unicode. También ofrece una implementación eficiente de COW , de la que he llegado a depender mucho.

Contenedores de Qt:

  • ofrecen la misma implementación COW que en QString, que es extremadamente útil cuando se trata de usar la macro foreach de Qt (que hace una copia) y cuando se usan metatipos o señales y ranuras.
  • puede usar iteradores de estilo STL o iteradores de estilo Java
  • son transmisibles con QDataStream
  • se usan ampliamente en la API de Qt
  • tener una implementación estable a través de los sistemas operativos. Una implementación de STL debe obedecer al estándar de C ++, pero de lo contrario es libre de hacer lo que quiera (consulte la controversia de std :: string COW). Algunas implementaciones de STL son especialmente malas.
  • Proporcione hashes, que no están disponibles a menos que use TR1

El QTL tiene una filosofía diferente a la STL, que J. Blanchette resume bien : "Mientras que los contenedores de STL están optimizados para velocidad bruta, las clases de contenedores de Qt han sido cuidadosamente diseñadas para proporcionar comodidad, mínimo uso de memoria y mínima expansión de código".
El enlace anterior proporciona más detalles sobre la implementación del QTL y qué optimizaciones se utilizan.


Contenedores STL:

  • Tener garantías de rendimiento
  • Se puede usar en algoritmos STL que también tienen garantías de rendimiento
  • Puede ser aprovechado por bibliotecas C ++ de terceros como Boost
  • Son estándares, y es probable que sobrevivan a soluciones patentadas
  • Fomentar la programación genérica de algoritmos y estructuras de datos. Si escribe nuevos algoritmos y estructuras de datos que se ajusten a STL, puede aprovechar lo que STL ya proporciona sin costo.

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?


Existe una (a veces) gran limitación en QVector. Solo puede asignar bytes de memoria (tenga en cuenta que el límite está en bytes, no en cantidad de elementos). Esto implica que tratar de asignar bloques contiguos de memoria mayores de ~ 2GB con un QVector dará lugar a un bloqueo. Esto sucede con Qt 4 y 5. std :: vector no tiene tal limitación.


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.


Los contenedores de Qt son más limitados que los de STL. Algunos ejemplos de dónde los STL son superiores (todos estos en el pasado):

  • STL está estandarizado, no cambia con cada versión de Qt (Qt 2 tenía QList (basado en puntero) y QValueList (basado en valor); Qt 3 tenía QPtrList y QValueList ; Qt 4 ahora tenía QList , y no se parecía en nada a QPtrList o QValueList ).
    Incluso si termina usando los contenedores Qt, use el subconjunto API compatible con STL (es decir, push_back() , no append() ; front() , no first() , ...) para evitar volver a portarlos, vuelva Qt 5 En las transiciones Qt2-> 3 y Qt3-> 4, los cambios en los contenedores Qt se encontraban entre los que requerían la mayoría del cambio de código.
  • Los contenedores bidireccionales STL tienen todos rbegin() / rend() , lo que hace que la iteración inversa sea simétrica a la iteración directa. No todos los contenedores Qt los tienen (los asociativos no), por lo que la iteración inversa es innecesariamente complicada.
  • Los contenedores STL tienen range- insert() de tipos de iteradores diferentes, pero compatibles, haciendo que std::copy() mucho menos necesario.
  • Los contenedores STL tienen un argumento de plantilla de Allocator , lo que hace que la gestión de memoria personalizada sea trivial (se requiere typedef), en comparación con Qt (se requiere el fork de QLineEdit para s/QString/secqstring/ ). EDIT 20171220 : Esto reduce Qt de los avances en el diseño del asignador siguiendo C ++ 11 y C ++ 17, cf. por ejemplo, la charla de John Lakos ( parte 2 ).
  • No hay Qt equivalente a std::deque .
  • std::list tiene splice() . Cada vez que me encuentro usando std::list , es porque necesito splice() .
  • std::stack , std::queue agregan apropiadamente su contenedor subyacente, y no lo heredan, como lo hace QStack , QQueue .
  • QSet es como std::unordered_set , no como std::set .
  • QList es simplemente extraño .

Muchos de los anteriores podrían resolverse con bastante facilidad en Qt , pero la biblioteca de contenedores en Qt parece experimentar una falta de enfoque de desarrollo en este momento.

EDIT 20150106 : después de haber pasado un tiempo tratando de llevar C ++ 11-soporte para Qt 5 clases de contenedor, he decidido que no vale la pena el trabajo. Si nos fijamos en el trabajo que se está poniendo en las implementaciones de la biblioteca estándar de C ++, está bastante claro que las clases de Qt nunca se pondrán al día. Hemos lanzado Qt 5.4 ahora y QVector aún no mueve elementos en reasignaciones, no tiene emplace_back() ni rvalue- push_back() ... También QOptional recientemente una plantilla de clase QOptional , esperando std::optional lugar . Del mismo modo para std::unique_ptr . Espero que esa tendencia continúe.


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.


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.


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 ...


Supongo que depende de la forma en que uses Qt. Si lo usa en todo su producto, entonces probablemente tenga sentido usar contenedores Qt. Si solo lo contiene (por ejemplo) la parte de UI, puede ser mejor usar contenedores estándar de C ++.


Uno de los principales problemas es que la API de Qt espera que proporciones datos en los contenedores de Qt, por lo que también puedes usar los contenedores de Qt en lugar de transformarlos entre los dos.

Además, si ya está usando los contenedores Qt, podría ser un poco más óptimo usarlos exclusivamente, ya que no tendría que incluir los archivos de encabezado STL y potencialmente vincularlos en las bibliotecas STL. Sin embargo, dependiendo de su cadena de herramientas, eso puede suceder de todos modos. Puramente desde una perspectiva de diseño, la consistencia es generalmente algo bueno.


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.