Analizziamo queste affermazioni in fenomeni misurabili reali:
- Più leggero: i contenitori Qt utilizzano meno memoria dei contenitori STL
- Più sicuro: i contenitori Qt hanno meno opportunità di essere utilizzati in modo improprio
- Più facile: i contenitori Qt presentano meno di un carico intellettuale
Più facile
L'affermazione fatta in questo contesto è che l'iterazione in stile java è in qualche modo "più semplice" rispetto allo stile STL, e quindi Qt è più facile da usare grazie a questa interfaccia aggiuntiva.
Stile Java:
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
Stile STL:
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
qDebug << *i;
Lo stile iteratore Java ha il vantaggio di essere un po 'più piccolo e più pulito. Il problema è che questo non è più lo stile STL.
Stile C ++ 11 STL
for( auto i = list.begin(); i != list.end(); ++i)
qDebug << *i;
o
C ++ 11 foreach style
for (QString i : list)
qDebug << i;
Che è così drasticamente semplice che non c'è motivo di usare altro (a meno che non si supporti C ++ 11).
Il mio preferito, tuttavia, è:
BOOST_FOREACH(QString i, list)
{
qDebug << i;
}
Quindi, come possiamo vedere, questa interfaccia non ci guadagna nulla se non un'interfaccia aggiuntiva, in aggiunta a un'interfaccia già elegante, snella e moderna. Aggiunta di un livello di astrazione non necessario oltre a un'interfaccia già stabile e utilizzabile? Non è la mia idea di "più facile".
Inoltre, le interfacce Qt foreach e java aggiungono sovraccarico; copiano la struttura e forniscono un livello non necessario di indiretta. Questo potrebbe non sembrare molto, ma perché aggiungere un livello di sovraccarico per fornire un'interfaccia non molto più semplice? Java ha questa interfaccia perché java non ha un sovraccarico dell'operatore; C ++ lo fa.
Safer
La giustificazione fornita da Qt è il problema della condivisione implicita, che non è né implicito né un problema. Implica tuttavia la condivisione.
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.
*/
Innanzitutto, questo non è implicito; stai assegnando esplicitamente un vettore a un altro. Le specifiche dell'iteratore STL indicano chiaramente che gli iteratori appartengono al contenitore, quindi abbiamo chiaramente introdotto un contenitore condiviso tra b e a. Secondo, questo non è un problema; fintanto che verranno seguite tutte le regole delle specifiche dell'iteratore, nulla andrà per il verso sbagliato. L'unica volta che qualcosa va storto è qui:
b.clear(); // Now the iterator i is completely invalid.
Qt lo specifica come se significasse qualcosa, come un problema sorge de novo da questo scenario. Non L'iteratore è invalidato e, proprio come tutto ciò a cui è possibile accedere da più aree disgiunte, è così che funziona. In effetti, ciò accadrà facilmente con gli iteratori in stile Java in Qt, grazie alla sua forte dipendenza dalla condivisione implicita, che è un antipattern come documentato qui e in molte altre aree . Sembra particolarmente strano che questa "ottimizzazione" venga messa in atto in un framework sempre più orientato al multithreading, ma questo è marketing per te.
Accendino
Questo è un po 'più complicato. L'uso delle strategie di condivisione e crescita implicite di copia e scrittura rende molto difficile fornire effettivamente garanzie sulla quantità di memoria che il contenitore utilizzerà in un determinato momento. Questo è diverso dall'STL, che offre forti garanzie algoritmiche.
Sappiamo che il limite minimo di spazio sprecato per un vettore è la radice quadrata della lunghezza del vettore , ma sembra che non ci sia modo di implementarlo in Qt; le varie "ottimizzazioni" che supportano impedirebbero questa importantissima funzione di risparmio di spazio. L'STL non richiede questa funzionalità (e la maggior parte utilizza una crescita raddoppiata, che è più dispendiosa), ma è importante notare che potresti almeno implementare questa funzionalità, se necessario.
Lo stesso vale per gli elenchi doppiamente collegati, che potrebbero utilizzare il collegamento XOr per ridurre drasticamente lo spazio utilizzato. Ancora una volta, questo è impossibile con Qt, a causa dei suoi requisiti di crescita e COW.
Le COW possono davvero rendere qualcosa di più leggero, ma anche i contenitori intrusivi, come supportati da boost , e Qt li usavano frequentemente nelle versioni precedenti, ma non vengono più utilizzati tanto perché sono difficili da usare, non sicuri e impongono un onere sul programmatore. COW è una soluzione molto meno invadente, ma poco attraente per le ragioni sopra esposte.
Non vi è alcun motivo per cui non è possibile utilizzare contenitori STL con lo stesso costo di memoria o meno dei contenitori di Qt, con l'ulteriore vantaggio di sapere effettivamente quanta memoria si sprecherà in un determinato momento. Sfortunatamente, è impossibile confrontare i due nell'uso della memoria non elaborata, poiché tali benchmark mostrerebbero risultati incredibilmente diversi in diversi casi d'uso, che è esattamente il tipo di problema che l'STL è stato progettato per correggere.
In conclusione
Evita l'uso dei contenitori Qt quando possibile, senza imporre un costo di copia, e usa l'iterazione del tipo STL (forse attraverso un wrapper o la nuova sintassi), quando possibile.