Comencé usando std::(w)string
y los contenedores STL exclusivamente y convirtiendo a / desde los equivalentes de Qt, pero ya he cambiado QString
y descubro que estoy usando cada vez más los contenedores de Qt.
Cuando se trata de cadenas, QString
ofrece una funcionalidad mucho más completa en comparación con std::basic_string
y es completamente consciente de Unicode. También ofrece una implementación eficiente de COW , en la que he llegado a confiar mucho.
Contenedores de Qt:
- ofrecen la misma implementación de COW que en
QString
, lo cual es extremadamente útil cuando se trata de usar la foreach
macro 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 en todos los sistemas operativos. Una implementación STL debe obedecer el estándar C ++, pero por lo demás es libre de hacer lo que le plazca (vea la
std::string
controversia COW). Algunas implementaciones de STL son especialmente malas.
- proporcionar hashes, que no están disponibles a menos que use TR1
El QTL tiene una filosofía diferente a la del STL, que J. Blanchette resume bien : "Mientras que los contenedores de STL están optimizados para la velocidad bruta, las clases de contenedores de Qt se han diseñado cuidadosamente para proporcionar comodidad, un uso mínimo de memoria y una expansión mínima del código".
El enlace anterior proporciona más detalles sobre la implementación de QTL y las optimizaciones que se utilizan.
QList<double>
en una arquitectura de 32 bits para uso de memoria para ver por ti mismo.QVector
lugar deQList
. Hay una bonita explicación de Qt, que QList está diseñado para almacenar punteros en objetos. Por lo tanto, cada elemento doble creado dinámicamente y puntero a este elemento se almacena enQList
. QList está diseñado como contenedor "intermedio" entre el vector y la lista vinculada. No está diseñado para casos críticos de memoria / rendimiento.Esta es una pregunta difícil de responder. Realmente puede reducirse a un argumento filosófico / subjetivo.
Habiendo dicho eso...
Recomiendo la regla "Cuando en Roma ... Haz lo que hacen los romanos"
Lo que significa que si estás en tierra Qt, codifica como lo hacen los Qt'ians. Esto no es solo por cuestiones de legibilidad / consistencia. Considere lo que sucede si almacena todo en un contenedor stl y luego tiene que pasar todos esos datos a una función Qt. ¿Realmente desea administrar un montón de código que copia cosas dentro / fuera de los contenedores Qt? Su código ya depende en gran medida de Qt, por lo que no es como si lo estuviera haciendo más "estándar" utilizando contenedores stl. ¿Y cuál es el objetivo de un contenedor si cada vez que desea usarlo para algo útil, tiene que copiarlo en el contenedor Qt correspondiente?
fuente
Los contenedores Qt son más limitados que los STL. Algunos ejemplos de donde los STL son superiores (todos estos los he golpeado en el pasado):
QList
(basado en puntero) yQValueList
(basado en valor); Qt 3 teníaQPtrList
yQValueList
; Qt 4 ahora tieneQList
, y no es nada parecidoQPtrList
oQValueList
).Incluso si termina usando los contenedores Qt, use el subconjunto de API compatible con STL (es decir
push_back()
, noappend()
;,front()
nofirst()
, ...) para evitar la transferencia una vez más, venga Qt 5. Tanto en Qt2-> 3 como en Qt3-> 4 transiciones, los cambios en los contenedores Qt se encontraban entre los que requerían la mayor rotación de código.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.insert()
de tipos de iteradores diferentes, pero compatibles, lo que hace que seastd::copy()
mucho menos necesario.Allocator
argumento de plantilla, lo que hace que la administración de memoria personalizada sea trivial (se requiere typedef), en comparación con Qt (seQLineEdit
requiere fork des/QString/secqstring/
). EDITAR 20171220 : Esto corta Qt de los avances en el diseño del asignador después de C ++ 11 y C ++ 17, cf. por ejemplo, la charla de John Lakos ( parte 2 ).std::deque
.std::list
tienesplice()
. Cada vez que me encuentro usandostd::list
, es porque lo necesitosplice()
.std::stack
,std::queue
Agregar adecuadamente su contenedor subyacente, y no heredan, comoQStack
,QQueue
hacer.QSet
es comostd::unordered_set
, no es asístd::set
.QList
es un simplemente extraño .Muchos de los anteriores podrían resolverse fácilmente en Qt , pero la biblioteca de contenedores en Qt parece experimentar una falta de enfoque de desarrollo en este momento.
EDITAR 20150106 : Después de pasar un tiempo tratando de llevar el soporte C ++ 11 a las clases de contenedores Qt 5, he decidido que no vale la pena el trabajo. Si observa el trabajo que se está poniendo en las implementaciones de la biblioteca estándar de C ++, está bastante claro que las clases Qt nunca se pondrán al día. Hemos lanzado Qt 5.4 ahora y
QVector
todavía no mueve elementos en reasignaciones, no tieneemplace_back()
o rvalue-push_back()
... También recientementerechazamosunaQOptional
plantilla de clase, esperando en sustd::optional
lugar. Del mismo modo parastd::unique_ptr
. Espero que esa tendencia continúe.fuente
QList
era el equivalente astd::deque
. Claramente, no debería haber simplemente hojeado la documentación.QVector
ha tenidocrbegin
y amigos desde Qt 5.6std::reverse_iterator
durante los rotosQHash
/QMap
iteradores, que, cuando se eliminan las referencias, regresanmapped_type
en lugar devalue_type
). Nada que no se pueda arreglar, pero vea mi EDIT de 2015.QVector
utilizaint
como índice, lo que limita los tamaños de 31 bits (incluso en sistemas de 64 bits). Además, ni siquiera puede almacenarINT_MAX
elementos de tamaño superior a 1 byte. Por ejemplo, el más grande.size()
que pude tenerQVector<float>
en x86_64 Linux gcc fue 536870907 elementos (2²⁹-5), mientras que asigné constd::vector<float>
éxito 4294967295 elementos (2³²-1; no intenté más debido a la falta de RAM para esto (este tamaño ya toma 16 GiB) )Dividamos estas afirmaciones en fenómenos mensurables reales:
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
Estilo STL:
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
o
C ++ 11 estilo foreach
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:
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.
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í:
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.
fuente
Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
los iteradores de estilo Java de Qt no se agregaron a C ++ 11; Lo preceden. De todos modos, Qt'sforeach(QString elem, list)
es tan fácil como el foreach de C ++ 11 o BOOST_FOREACH y funciona con compiladores anteriores a C ++ 11.So, as we can see, this interface gains us nothing except an additional interface, *on top of* an already sleek, streamlined, and modern interface. Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
(énfasis mío) Dijiste esto justo después de mostrarnos las versiones C ++ 11 y BOOST de foreach, haciendo que parezca que la versión Qt está construida a partir de uno de esos dos, lo cual no es el caso AFAICT. Estoy seguro de que eso no es lo que querías decir, pero así es como sale. De ahí la "información engañosa".It's an additional layer of abstraction (and an unnecessary one) that bloats the interface, which is not easier.
Todavía no está claro con qué te estás comparando. C ++ 03 iteradores? C ++ 11 iteradores? BOOST_FOREACH? Todas las anteriores?Contenedores STL:
fuente
Los contenedores Qt usan modismos de copia en escritura.
fuente
std::basic_string
y el estándar tomó medidas con C ++ 11 para que esto no sea conforme.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 transformarte de un lado a otro.
Además, si ya está utilizando 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 posiblemente 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 generalmente es algo bueno.
fuente
Si los datos con los que está trabajando se utilizan principalmente para controlar la interfaz de usuario basada en Qt, definitivamente use contenedores Qt.
Si los datos se usan principalmente internamente en la aplicación, y nunca es probable que se aleje de Qt, entonces, salvo problemas de rendimiento, use los contenedores de Qt porque hará que los bits de datos que van a la interfaz de usuario sean más fáciles de manejar.
Si los datos se usan principalmente junto con otras bibliotecas que solo conocen los contenedores STL, entonces use contenedores STL. Si tiene esta situación, está en problemas, pase lo que pase, ya que va a hacer muchos cambios de un lado a otro entre los tipos de contenedores, sin importar lo que haga.
fuente
Además de la diferencia de COW, los contenedores STL son mucho más compatibles en 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 rehuir Qt solo por esta razón; solo que, si todas las demás cosas son iguales, debe usar el STL de manera predeterminada, pero, por supuesto, todas las cosas rara vez son iguales, por lo que tendrá que decidir en su propio contexto cuál tiene 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 dada 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).
fuente
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. Es posible que obtenga resultados de rendimiento diferentes.
EDITAR: No estoy diciendo que STL es "más lento", pero señalo los efectos de varios detalles de implementación.
Por favor verifique esto , y luego tal vez esto .
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.
fuente
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 lo contiene solo (por ejemplo) en la parte de la interfaz de usuario, puede ser mejor usar contenedores estándar de C ++.
fuente
Soy de la opinión de que STL es una excelente pieza de software, sin embargo, si debo hacer alguna 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, por ejemplo, SUN Studio CC, es probable que STL le traiga dolores de cabeza debido al compilador, no al STL per se. En ese caso, dado que el compilador te hará daño en la cabeza, solo usa Qt para ahorrarte el problema. Solo mis 2 centavos ...
fuente
Hay una (a veces) gran limitación en QVector. Solo puede asignar bytes int de memoria (tenga en cuenta que el límite está en bytes no en número de elementos). Esto implica que intentar asignar bloques contiguos de memoria mayores de ~ 2GB con un QVector provocará un bloqueo. Esto sucede con Qt 4 y 5. std :: vector no tiene esa limitación.
fuente
La razón principal para usar 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 (
new
llamadas) pase lo que pase. En STL, siempre puede crear un asignador personalizado que asigne internamente toda esa memoria a la vez y asignarlo a cada entrada a medida que se llena el mapa.Mi consejo es usar contenedores STL al escribir algoritmos críticos de rendimiento en la lógica de negocios y luego convertirlos nuevamente a contenedores Qt cuando los resultados estén listos para que los muestren los controles y formularios de la IU si es necesario.
fuente
QMapNode<K,V>
en tuK
,V
para proporcionar el tuyo propiooperator new
.