Podzielmy te twierdzenia na rzeczywiste mierzalne zjawiska:
- Lżejsze: pojemniki Qt zużywają mniej pamięci niż pojemniki STL
- Bezpieczniej: pojemniki Qt mają mniejszą szansę na niewłaściwe użycie
- Łatwiej: pojemniki Qt stanowią mniejsze obciążenie intelektualne
Łatwiej
Twierdzi się w tym kontekście, że iteracja w stylu Java jest w jakiś sposób „łatwiejsza” niż styl STL, a zatem Qt jest łatwiejszy w użyciu z powodu tego dodatkowego interfejsu.
Styl Java:
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
Styl STL:
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
qDebug << *i;
Styl iteratora Java ma tę zaletę, że jest nieco mniejszy i bardziej przejrzysty. Problem polega na tym, że tak naprawdę nie jest to już styl STL.
Styl C ++ 11 STL
for( auto i = list.begin(); i != list.end(); ++i)
qDebug << *i;
lub
Styl foreach C ++ 11
for (QString i : list)
qDebug << i;
Co jest tak drastycznie proste, że nie ma powodu, aby używać czegoś innego (chyba że nie obsługujesz C ++ 11).
Moim ulubionym jest jednak:
BOOST_FOREACH(QString i, list)
{
qDebug << i;
}
Tak więc, jak widzimy, ten interfejs nie daje nam nic poza dodatkowym interfejsem, na dodatek do już eleganckiego, usprawnionego i nowoczesnego interfejsu. Dodanie niepotrzebnego poziomu abstrakcji na już stabilnym i użytecznym interfejsie? Nie mój pomysł na „łatwiej”.
Ponadto interfejsy foret i java Qt zwiększają koszty; kopiują strukturę i zapewniają niepotrzebny poziom pośredni. Może to nie wydawać się dużo, ale po co dodawać warstwę kosztów ogólnych, aby zapewnić niezbyt prostszy interfejs? Java ma ten interfejs, ponieważ java nie ma przeciążenia operatora; C ++ tak.
Bezpieczniej
Uzasadnieniem, które podaje Qt, jest niejawny problem z udostępnianiem, który nie jest ani niejawny, ani stanowi problem. Wymaga to jednak udostępniania.
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.
*/
Po pierwsze, nie jest to dorozumiane; wyraźnie przypisujesz jeden wektor do drugiego. Specyfikacja iteratora STL wyraźnie wskazuje, że iteratory należą do kontenera, więc wyraźnie wprowadziliśmy wspólny kontener między b i a. Po drugie, to nie jest problem; tak długo, jak przestrzegane są wszystkie reguły specyfikacji iteratora, absolutnie nic nie pójdzie źle. Jedyny raz coś poszło nie tak:
b.clear(); // Now the iterator i is completely invalid.
Qt określa to tak, jakby to coś znaczyło, tak jak problem powstaje de novo w tym scenariuszu. Tak nie jest. Iterator jest unieważniony i tak jak wszystko, co jest dostępne z wielu rozłącznych obszarów, tak właśnie działa. W rzeczywistości stanie się to łatwo z iteratorami stylu Java w Qt, dzięki silnemu poleganiu na niejawnym współużytkowaniu, co jest antypatternem opisanym tutaj i na wielu innych obszarach . Wydaje się szczególnie dziwne, że ta „optymalizacja” jest stosowana w ramach, które coraz bardziej zbliżają się do wielowątkowości, ale to dla ciebie marketing.
Zapalniczka
Ten jest nieco trudniejszy. Korzystanie ze strategii kopiowania przy zapisie oraz niejawnego udostępniania i wzrostu powoduje, że bardzo trudno jest zagwarantować, ile pamięci zużyje Twój kontener w danym momencie. W przeciwieństwie do STL, który daje silne gwarancje algorytmiczne.
Wiemy, że minimalną granicą zmarnowanego miejsca dla wektora jest pierwiastek kwadratowy z długości wektora , ale wydaje się, że nie ma możliwości zaimplementowania tego w Qt; różne obsługiwane przez nich „optymalizacje” wykluczałyby tę bardzo ważną funkcję oszczędzania miejsca. STL nie wymaga tej funkcji (i większość korzysta z podwójnego wzrostu, co jest bardziej marnotrawstwem), ale ważne jest, aby pamiętać, że możesz przynajmniej zaimplementować tę funkcję, jeśli zajdzie taka potrzeba.
To samo dotyczy podwójnie połączonych list, które mogłyby użyć linkowania XOr, aby drastycznie zmniejszyć zużycie miejsca. Ponownie, jest to niemożliwe w przypadku Qt, ze względu na jego wymagania dotyczące wzrostu i COW.
COW może rzeczywiście uczynić coś lżejszym, ale również pojemniki Intrusive, takie jak obsługiwane przez boost , i Qt często ich używały we wcześniejszych wersjach, ale nie są już używane tak często, ponieważ są trudne w użyciu, niebezpieczne i nakładają obciążenie na programatorze. COW jest znacznie mniej inwazyjnym rozwiązaniem, ale nieatrakcyjnym z powyższych powodów.
Nie ma powodu, dla którego nie można było używać kontenerów STL o takim samym koszcie pamięci lub mniejszym niż pojemniki Qt, a dodatkową korzyścią jest faktyczna wiedza o ilości pamięci, którą można zmarnować w danym momencie. Nie da się niestety porównać tych dwóch pod względem wykorzystania pamięci surowej, ponieważ takie testy porównawcze pokazałyby bardzo różne wyniki w różnych przypadkach użycia, co jest dokładnym rodzajem problemu, który STL miał rozwiązać.
Podsumowując
Unikaj korzystania z kontenerów Qt, gdy jest to możliwe, bez nakładania kosztów kopiowania i, w miarę możliwości, używaj iteracji typu STL (być może poprzez opakowanie lub nową składnię).