Comecei usando std::(w)string
exclusivamente os contêineres STL e convertendo para / dos equivalentes Qt, mas já mudei para QString
e acho que estou usando os contêineres do Qt cada vez mais.
Quando se trata de strings, QString
oferece uma funcionalidade muito mais completa em comparação com std::basic_string
e é totalmente compatível com Unicode. Ele também oferece uma implementação eficiente de COW , na qual eu dependo fortemente.
Contentores da Qt:
- oferecem a mesma implementação de COW que em
QString
, o que é extremamente útil quando se trata de usar a foreach
macro do Qt (que faz uma cópia) e ao usar meta-tipos ou sinais e slots.
- pode usar iteradores no estilo STL ou iteradores no estilo Java
- são streamable com
QDataStream
- são usados extensivamente na API do Qt
- ter uma implementação estável nos sistemas operacionais. Uma implementação de STL deve obedecer ao padrão C ++, mas é livre de fazer o que bem entender (consulte a
std::string
controvérsia da COW). Algumas implementações de STL são especialmente ruins.
- fornecer hashes, que não estão disponíveis, a menos que você use TR1
O QTL tem uma filosofia diferente da STL, que é bem resumida por J. Blanchette: "Enquanto os contêineres da STL são otimizados para velocidade bruta, as classes de contêineres da Qt foram cuidadosamente projetadas para oferecer conveniência, uso mínimo de memória e expansão mínima de código".
O link acima fornece mais detalhes sobre a implementação do QTL e quais otimizações são usadas.
QList<double>
em uma arquitetura de 32 bits para o uso da memória ver por si mesmo.QVector
vez deQList
. Há uma explicação bastante do Qt, que o QList foi projetado para armazenar ponteiros nos objetos. Portanto, cada item duplo criado dinamicamente e ponteiro para esse item são armazenadosQList
. O QList foi projetado como contêiner "intermediário" entre o vetor e a lista vinculada. Ele não foi projetado para casos críticos de memória / desempenho.Esta é uma pergunta difícil de responder. Pode realmente se resumir a um argumento filosófico / subjetivo.
Dito isto ...
Eu recomendo a regra "Quando em Roma ... faça como os romanos"
O que significa que se você estiver na terra Qt, codifique como os Qt'ians fazem. Isso não é apenas para questões de legibilidade / consistência. Considere o que acontece se você armazenar tudo em um contêiner stl, então você precisará passar todos esses dados para uma função Qt. Deseja realmente gerenciar um monte de código que copia as coisas para dentro / fora dos contêineres Qt. Seu código já é fortemente dependente do Qt, então não é como se você o estivesse tornando mais "padrão" usando stl containers. E qual é o sentido de um contêiner, se toda vez que você quiser usá-lo para algo útil, você deve copiá-lo no contêiner Qt correspondente?
fonte
Os contêineres Qt são mais limitados que os contêineres STL. Alguns exemplos de onde os STL são superiores (todos esses que eu bati no passado):
QList
(com base em ponteiro) eQValueList
(com base em valor); o Qt 3 tinhaQPtrList
eQValueList
; o Qt 4 agora temQList
, e não é nada parecidoQPtrList
ouQValueList
).Mesmo se você acabar usando os contêineres Qt, use o subconjunto da API compatível com STL (ou seja
push_back()
, nãoappend()
;,front()
nãofirst()
, ...) para evitar a portabilidade novamente, venha o Qt 5. Nos Qt2-> 3 e Qt3-> 4 nas transições, as alterações nos contêineres Qt estavam entre as que mais exigiam rotatividade de código.rbegin()
/rend()
, tornando a iteração reversa simétrica para a iteração direta. Nem todos os contêineres Qt os possuem (os associativos não), portanto, a iteração reversa é desnecessariamente complicada.insert()
entre diferentes, mas compatíveis, tipos de iteradores, tornandostd::copy()
muito menos necessário.Allocator
argumento de modelo, tornando trivial o gerenciamento de memória personalizado (é necessário typedef), em comparação com o Qt (bifurcação deQLineEdit
necessário paras/QString/secqstring/
). EDIT 20171220 : Isso reduz Qt dos avanços no design do alocador, seguindo C ++ 11 e C ++ 17, cf. por exemplo, a palestra de John Lakos ( parte 2 ).std::deque
.std::list
temsplice()
. Sempre que me vejo usandostd::list
, é porque precisosplice()
.std::stack
,std::queue
Agregar adequadamente o seu recipiente subjacente, e não herdam-lo, comoQStack
,QQueue
fazer.QSet
é comostd::unordered_set
, não comostd::set
.QList
é um pouco estranho .Muitos dos itens acima podem ser resolvidos com bastante facilidade no Qt , mas a biblioteca de contêineres no Qt parece experimentar uma falta de foco no desenvolvimento no momento.
EDIT 20150106 : Depois de passar algum tempo tentando levar o suporte ao C ++ 11 para as classes de contêineres do Qt 5, decidi que não vale a pena o trabalho. Se você observar o trabalho que está sendo colocado nas implementações de bibliotecas padrão do C ++, é bastante claro que as classes Qt nunca serão atualizadas. Lançamos o Qt 5.4 agora e
QVector
ainda não movemos elementos nas realocações, não tememplace_back()
ou rvalorizamospush_back()
... Também rejeitamos recentemente umQOptional
modelo de classe, esperando porstd::optional
ele. Da mesma forma parastd::unique_ptr
. Espero que essa tendência continue.fonte
QList
era o equivalente astd::deque
. Claramente, eu não deveria ter apenas folheado a documentação.QVector
tevecrbegin
e amigos desde Qt 5.6std::reverse_iterator
sobre os quebradosQHash
/QMap
iteradores, que, quando desreferenciados, retornam emmapped_type
vez devalue_type
). Nada que não possa ser consertado, mas veja a minha edição de 2015.QVector
usarint
como índice, limitando os tamanhos de 31 bits (mesmo em sistemas de 64 bits). Além disso, ele não pode nem armazenarINT_MAX
elementos de tamanho maior que 1 byte. Por exemplo, o maior que.size()
eu poderia terQVector<float>
no x86_64 gcc no Linux era 536870907 elementos (2²⁹-5), enquanto alocava comstd::vector<float>
sucesso 4294967295 elementos (2³²-1; não tentei mais devido à falta de RAM para isso (esse tamanho já leva 16 GiB) )Vamos dividir essas afirmações em fenômenos mensuráveis reais:
Mais fácil
A alegação feita neste contexto é que a iteração no estilo java é de alguma forma "mais fácil" que o estilo STL e, portanto, o Qt é mais fácil de usar devido a essa interface adicional.
Estilo Java:
Estilo STL:
O estilo do iterador Java tem o benefício de ser um pouco menor e mais limpo. O problema é que esse não é mais o estilo STL.
Estilo STL C ++ 11
ou
Estilo foreach C ++ 11
O que é tão drasticamente simples que não há razão para usar qualquer outra coisa (a menos que você não suporte C ++ 11).
Meu favorito, no entanto, é:
Portanto, como podemos ver, essa interface não ganha nada além de uma interface adicional, além de uma interface já elegante, simplificada e moderna. Adicionando um nível desnecessário de abstração em cima de uma interface já estável e utilizável? Não é a minha idéia de "mais fácil".
Além disso, as interfaces Qt foreach e java adicionam sobrecarga; eles copiam a estrutura e fornecem um nível desnecessário de indireção. Isso pode não parecer muito, mas por que adicionar uma camada de sobrecarga para fornecer uma interface não muito mais simples? Java possui essa interface porque o java não possui sobrecarga de operador; C ++ faz.
Mais seguro
A justificativa que Qt dá é o problema implícito de compartilhamento, que não é implícito nem é um problema. No entanto, envolve compartilhar.
Primeiro, isso não está implícito; você está atribuindo explicitamente um vetor a outro. A especificação do iterador STL indica claramente que os iteradores pertencem ao contêiner, portanto, introduzimos claramente um contêiner compartilhado entre be um. Segundo, isso não é um problema; contanto que todas as regras da especificação do iterador sejam seguidas, absolutamente nada dará errado. A única vez que algo dá errado é aqui:
Qt especifica isso como se isso significasse algo, como se um problema surgisse de novo nesse cenário. Não faz. O iterador é invalidado e, assim como qualquer coisa que possa ser acessada de várias áreas independentes, é assim que funciona. De fato, isso ocorrerá prontamente com os iteradores do estilo Java no Qt, graças à grande dependência do compartilhamento implícito, que é um antipadrão conforme documentado aqui e em muitas outras áreas . Parece especialmente estranho que essa "otimização" seja usada em uma estrutura que se move cada vez mais para o multithreading, mas isso é marketing para você.
Mais leve
Este é um pouco mais complicado. O uso das estratégias de cópia na gravação e compartilhamento implícito e crescimento torna muito difícil garantir realmente a quantidade de memória que seu contêiner usará a qualquer momento. Isso é diferente do STL, que oferece fortes garantias algorítmicas.
Sabemos que o limite mínimo de espaço desperdiçado para um vetor é a raiz quadrada do comprimento do vetor , mas parece não haver maneira de implementar isso no Qt; as várias "otimizações" suportadas impediriam esse recurso muito importante de economia de espaço. O STL não requer esse recurso (e a maioria usa um crescimento de duplicação, o que é mais inútil), mas é importante observar que você poderia pelo menos implementar esse recurso, se necessário.
O mesmo se aplica às listas duplamente vinculadas, que poderiam usar a vinculação XOr para reduzir drasticamente o espaço usado. Novamente, isso é impossível com o Qt, devido a seus requisitos de crescimento e COW.
O COW pode, de fato, tornar algo mais leve, mas o mesmo pode acontecer com os Intrusive Containers, como os suportados pelo boost , e o Qt os usa com frequência nas versões anteriores, mas não são mais usados tanto porque são difíceis de usar, inseguros e impõem um ônus. no programador. COW é uma solução muito menos invasiva, mas pouco atraente pelas razões expostas acima.
Não há razão para que você não possa usar contêineres STL com o mesmo custo de memória ou menos que os contêineres do Qt, com o benefício adicional de saber quanta memória você desperdiçará a qualquer momento. Infelizmente, é impossível comparar os dois no uso de memória bruta, porque esses benchmarks mostrariam resultados bastante diferentes em diferentes casos de uso, que é o tipo exato de problema que o STL foi projetado para corrigir.
Em conclusão
Evite o uso de Qt Containers sempre que possível, sem impor um custo de cópia, e use a iteração do tipo STL (talvez por meio de um wrapper ou da nova sintaxe), sempre que possível.
fonte
Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
os iteradores no estilo Java do Qt não foram adicionados ao C ++ 11; eles são anteriores. De qualquer forma, o Qtforeach(QString elem, list)
é tão fácil quanto o foreach do C ++ 11 ou o BOOST_FOREACH e funciona com compiladores compatíveis com o C ++ 11 anterior.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".
(ênfase minha) Você disse isso logo após nos mostrar as versões C ++ 11 e BOOST do foreach, fazendo parecer que a versão Qt foi criada com base em uma dessas duas, o que não é o caso AFAICT. Tenho certeza de que não foi isso que você quis dizer, mas é assim que acontece. Daí "informações enganosas".It's an additional layer of abstraction (and an unnecessary one) that bloats the interface, which is not easier.
Ainda não está claro o que você está comparando. C ++ 03 iteradores? Iteradores C ++ 11? BOOST_FOREACH? Tudo acima?Contentores STL:
fonte
Os contêineres Qt usam o idioma de cópia na gravação.
fonte
std::basic_string
e o padrão tomou ações com o C ++ 11 para tornar isso não conforme.Uma das principais questões é que a API do Qt espera que você forneça dados nos contêineres do Qt; portanto, você também pode simplesmente usar os contêineres do Qt em vez de se alternar entre os dois.
Além disso, se você já estiver usando os contêineres Qt, pode ser um pouco mais ideal usá-los exclusivamente, pois não será necessário incluir os arquivos de cabeçalho STL e potencialmente vincular as bibliotecas STL. No entanto, dependendo da sua cadeia de ferramentas, isso pode acontecer de qualquer maneira. Puramente da perspectiva do design, a consistência geralmente é uma coisa boa.
fonte
Se os dados com os quais você está trabalhando são usados principalmente para conduzir a interface do usuário baseada em Qt, use definitivamente recipientes Qt.
Se os dados são usados principalmente internamente no aplicativo e é provável que você não se desloque para o Qt, então, salvo problemas de desempenho, use os contêineres do Qt, pois isso facilitará o processamento dos bits de dados que vão para a interface do usuário.
Se os dados forem usados principalmente em conjunto com outras bibliotecas que conhecem apenas contêineres STL, use contêineres STL. Se você tiver essa situação, estará em apuros, não importa o que aconteça, porque você fará muitas mudanças entre os tipos de contêiner, não importa o que você faça.
fonte
Além da diferença de COW, os contêineres STL são muito mais suportados em uma variedade de plataformas. O Qt é portátil o suficiente se você limitar seu trabalho a plataformas "tradicionais", mas o STL também está disponível em muitas outras plataformas mais obscuras (por exemplo, os DSPs da Texas Instruments).
Como o STL é padrão e não controlado por uma única corporação, geralmente há mais programadores que podem ler, entender e modificar facilmente o código STL e mais recursos (livros, fóruns on-line, conferências, etc.) para apoiá-los. fazendo isso do que o Qt. Isso não quer dizer que se deva fugir do Qt apenas por esse motivo; apenas que, como todas as outras coisas são iguais, você deve usar o STL como padrão, mas é claro que todas as coisas raramente são iguais, então você terá que decidir em seu próprio contexto o que faz mais sentido.
Em relação à resposta da AlexKR: o desempenho do STL é garantido dentro de limites, mas uma determinada implementação pode fazer uso de detalhes dependentes da plataforma para acelerar seu STL. Portanto, nesse sentido, você pode obter resultados diferentes em plataformas diferentes, mas nunca será mais lento que a garantia explícita (erros de módulo).
fonte
Meus cinco centavos: os contêineres Qt devem funcionar de maneira semelhante em plataformas diferentes. Enquanto os contêineres STL dependem da implementação do STL. Você pode obter resultados de desempenho diferentes.
Edição: Eu não estou dizendo que STL é "mais lento", mas aponto para efeitos de vários detalhes de implementação.
Por favor, verifique isto , e então talvez isto .
E não é um problema real de STL. Obviamente, se você tiver uma diferença significativa no desempenho, haverá um problema no código que usa STL.
fonte
Eu acho que depende da maneira como você usa o Qt. Se você usá-lo em todo o produto, provavelmente faz sentido usar recipientes Qt. Se você o contiver apenas (por exemplo) na parte da interface do usuário, talvez seja melhor usar contêineres padrão C ++.
fonte
Sou da opinião de que o STL é um excelente software, no entanto, se devo fazer alguma programação relacionada ao KDE ou Qt, então o Qt é o caminho a seguir. Também depende do compilador que você está usando, pois o GCC STL funciona muito bem; no entanto, se você precisar usar o SUN Studio CC, o STL provavelmente causará dores de cabeça por causa do compilador e não do STL. Nesse caso, como o compilador fará sua cabeça doer, basta usar o Qt para evitar problemas. Apenas meus 2 centavos ...
fonte
Há uma (às vezes) grande limitação no QVector. Ele só pode alocar int bytes de memória (observe que o limite está em bytes e não no número de elementos). Isso implica que tentar alocar blocos contíguos de memória maiores que ~ 2 GB com um QVector levará a uma falha. Isso acontece com o Qt 4 e 5. std :: vector não tem essa limitação.
fonte
O principal motivo para usar contêineres STL para mim é se você precisar de um alocador personalizado para reutilizar a memória em contêineres muito grandes. Suponha, por exemplo, que você tenha um QMap que armazene 1000000 entradas (pares de chave / valor). No Qt, isso implica exatamente 1000000 milhões de alocações (
new
chamadas), não importa o quê. No STL, você sempre pode criar um alocador personalizado que aloca internamente toda a memória de uma só vez e atribui-a a cada entrada à medida que o mapa é preenchido.Meu conselho é usar contêineres STL ao escrever algoritmos críticos de desempenho na lógica de negócios e convertê-los novamente em contêineres Qt quando os resultados estiverem prontos para serem exibidos pelos controles e formulários da interface do usuário, se necessário.
fonte
QMapNode<K,V>
para o seuK
,V
para fornecer o seu própriooperator new
.