Vamos dividir essas afirmações em fenômenos mensuráveis reais:
- Mais leve: os contêineres Qt usam menos memória que os contêineres STL
- Mais seguro: os contêineres Qt têm menos oportunidade de serem usados indevidamente
- Mais fácil: os contêineres Qt apresentam menos ônus intelectual
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:
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;
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
for( auto i = list.begin(); i != list.end(); ++i)
qDebug << *i;
ou
Estilo foreach C ++ 11
for (QString i : list)
qDebug << i;
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, é:
BOOST_FOREACH(QString i, list)
{
qDebug << i;
}
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.
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.
*/
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:
b.clear(); // Now the iterator i is completely invalid.
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.