Zacząłem od używania std::(w)string
wyłącznie kontenerów STL i konwersji do / z odpowiedników Qt, ale już przełączyłem się na QString
i stwierdzam, że coraz częściej używam kontenerów Qt.
Jeśli chodzi o ciągi, QString
oferuje znacznie bardziej kompletną funkcjonalność w porównaniu do std::basic_string
i jest całkowicie świadomy Unicode. Oferuje również wydajną implementację COW , na której w dużej mierze polegam.
Pojemniki Qt:
- oferują tę samą implementację COW, co w
QString
, co jest niezwykle przydatne, jeśli chodzi o użycie foreach
makra Qt (które wykonuje kopię) oraz przy użyciu meta-typów lub sygnałów i gniazd.
- może korzystać z iteratorów w stylu STL lub iteratorów w stylu Java
- można przesyłać strumieniowo za pomocą
QDataStream
- są szeroko stosowane w API Qt
- mieć stabilną implementację we wszystkich systemach operacyjnych. Implementacja STL musi być zgodna ze standardem C ++, ale poza tym jest wolna, jeśli chce (patrz
std::string
kontrowersje COW). Niektóre implementacje STL są szczególnie złe.
- udostępniaj skróty, które nie są dostępne, chyba że używasz TR1
QTL ma inną filozofię niż STL, którą dobrze podsumował J. Blanchette: „Podczas gdy kontenery STL są zoptymalizowane pod kątem surowej prędkości, klasy kontenerów Qt zostały starannie zaprojektowane, aby zapewnić wygodę, minimalne zużycie pamięci i minimalne rozszerzenie kodu”.
Powyższy link zawiera więcej szczegółów na temat implementacji QTL i zastosowanych optymalizacji.
QList<double>
w architekturze 32-bitowej do wykorzystania pamięci, aby przekonać się sam.QVector
zamiastQList
. Istnieje dość wyjaśnienie Qt, że QList jest przeznaczony do przechowywania wskaźników na obiektach. Tak więc każdy podwójny element tworzony dynamicznie i wskaźnik do tego elementu jest przechowywany wQList
. QList został zaprojektowany jako „środkowy” pojemnik między wektorem a połączoną listą. Nie jest przeznaczony do krytycznych przypadków związanych z pamięcią / wydajnością.To trudne pytanie. Może naprawdę sprowadzać się do filozoficznego / subiektywnego argumentu.
Biorąc to pod uwagę ...
Polecam zasadę „Będąc w Rzymie ... rób to, co Rzymianie”
Co oznacza, że jeśli jesteś w krainie Qt, koduj tak jak Qt'ianie. Dotyczy to nie tylko kwestii czytelności / spójności. Zastanów się, co się stanie, jeśli przechowujesz wszystko w kontenerze STL, musisz przekazać wszystkie te dane do funkcji Qt. Czy naprawdę chcesz zarządzać pakietem kodu, który kopiuje rzeczy do / poza pojemniki Qt. Twój kod jest już mocno zależny od Qt, więc nie jest tak, że czynisz go bardziej „standardowym” za pomocą kontenerów stl. Jaki jest sens kontenera, jeśli za każdym razem, gdy chcesz go użyć do czegoś użytecznego, musisz skopiować go do odpowiedniego kontenera Qt?
źródło
Kontenery Qt są bardziej ograniczone niż kontenery STL. Kilka przykładów, w których STL są lepsze (wszystkie te trafiłem w przeszłości):
QList
(oparte na wskaźnikach) iQValueList
(oparte na wartościach); Qt 3 miałQPtrList
iQValueList
; Qt 4 ma terazQList
i nie ma to jakQPtrList
lubQValueList
).. Nawet jeśli kończy się przy użyciu Qt pojemników, należy użyć STL kompatybilne API podzbiór (tj
push_back()
, nieappend()
,front()
niefirst()
, ...), aby uniknąć przenoszenia ponownie Qt 5. Zarówno w Qt2-> 3, jak i Qt3-> 4 przejścia, zmiany w kontenerach Qt były jednymi z tych, które wymagały największej rezygnacji z kodu.rbegin()
/rend()
, dzięki czemu iteracja do tyłu jest symetryczna do iteracji do przodu. Nie wszystkie kontenery Qt mają je (nie mają ich asocjacyjne), więc odwrotna iteracja jest niepotrzebnie skomplikowana.insert()
różne, ale kompatybilne typy iteratorów, przez costd::copy()
znacznie rzadziej są potrzebne.Allocator
argument szablonu, dzięki czemu niestandardowe zarządzanie pamięcią jest banalne (wymagany typedef), w porównaniu z Qt (QLineEdit
wymagane rozwidlenies/QString/secqstring/
). EDIT 20171220 : Zmniejsza to Qt postępów w projektowaniu alokatora zgodnie z C ++ 11 i C ++ 17, por. np. przemówienie Johna Lakosa ( część 2 ).std::deque
.std::list
masplice()
. Ilekroć się używamstd::list
, to dlatego, że potrzebujęsplice()
.std::stack
,std::queue
Właściwie agregowanie ich podstawową kontenera, a nie dziedziczą to, jakQStack
,QQueue
zrobić.QSet
jest jakstd::unordered_set
, nie jakstd::set
.QList
jest po prostu dziwne .Wiele z powyższych problemów można rozwiązać dość łatwo w Qt , ale biblioteka kontenerów w Qt wydaje się w tej chwili odczuwać brak ukierunkowania na rozwój.
EDYCJA 20150106 : Po pewnym czasie próbowania wprowadzenia obsługi języka C ++ 11 do klas kontenerów Qt 5, zdecydowałem, że nie jest to warte pracy. Jeśli spojrzysz na pracę, która jest umieszczana w standardowej implementacji biblioteki C ++, jasne jest, że klasy Qt nigdy nie nadrobią zaległości. Wydaliśmyteraz Qt 5.4 i
QVector
nadal nie przenosimy elementów w trakcie realokacji, nie mamyemplace_back()
lub nie warto się ...-push_back()
Niedawno również odrzuciliśmyQOptional
szablon klasy, czekając na tostd::optional
. Podobnie dlastd::unique_ptr
. Mam nadzieję, że ten trend się utrzyma.źródło
QList
był odpowiednikiemstd::deque
. Oczywiście nie powinienem po prostu przeglądać dokumentacji.QVector
macrbegin
i przyjaciół od Qt 5.6std::reverse_iterator
nad uszkodzonymiQHash
/QMap
iteratorami, które po dereferencji zwracająmapped_type
zamiastvalue_type
). Nic nie może być ustalona, ale zobaczyć moją EDIT od 2015QVector
Używaint
jako indeksu, ograniczając w ten sposób 31-bitowe rozmiary (nawet w systemach 64-bitowych). Co więcej, nie może nawet przechowywaćINT_MAX
elementów o rozmiarze większym niż 1 bajt. Np. Największy,.size()
jaki mogłem miećQVector<float>
na x86_64 Linux gcc, to 536870907 elementów (2²⁹-5), podczas gdy zstd::vector<float>
powodzeniem przydzielono 4294967295 elementów (2³²-1; nie próbowałem więcej z powodu braku pamięci RAM dla tego (ten rozmiar już zajmuje 16 GiB) ).Podzielmy te twierdzenia na rzeczywiste mierzalne zjawiska:
Ł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:
Styl STL:
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
lub
Styl foreach C ++ 11
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:
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.
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:
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ę).
źródło
Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
iteratory w stylu Java Qt nie zostały dodane do C ++ 11; wyprzedzają to. W każdym razie Qtforeach(QString elem, list)
jest tak samo łatwe jak foreach C ++ 11 lub BOOST_FOREACH i działa z kompilatorami zgodnymi z wcześniejszymi wersjami 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".
(moje podkreślenie) Powiedziałeś to zaraz po tym, jak pokazałeś nam wersje Foreach C ++ 11 i BOOST, dzięki czemu brzmi to tak, jakby wersja Qt była zbudowana z jednego z tych dwóch, co nie jest przypadkiem AFAICT. Jestem pewien, że nie o to ci chodziło, ale tak to wychodzi. Stąd „informacje wprowadzające w błąd”.It's an additional layer of abstraction (and an unnecessary one) that bloats the interface, which is not easier.
Nadal nie jest jasne, z czym się porównujesz. Iteratory C ++ 03? Iteratory C ++ 11? BOOST_FOREACH? Wszystkie powyższe?Kontenery STL:
źródło
Kontenery Qt używają idiomu „kopiuj przy zapisie”.
źródło
std::basic_string
norma, która podjęła działania w C ++ 11, aby uczynić to niezgodnym.Jednym z głównych problemów jest to, że API Qt oczekuje, że dostarczysz dane do kontenerów Qt, więc możesz po prostu użyć kontenerów Qt zamiast przekształcać się w obie strony.
Ponadto, jeśli już korzystasz z kontenerów Qt, może być nieco bardziej optymalne używanie ich wyłącznie, ponieważ nie musisz dołączać plików nagłówkowych STL i potencjalnie linkować do bibliotek STL. Jednak w zależności od zestawu narzędzi może się to zdarzyć. Z perspektywy projektowania spójność jest ogólnie dobrą rzeczą.
źródło
Jeśli dane, z którymi pracujesz, służą głównie do obsługi interfejsu użytkownika opartego na Qt, zdecydowanie użyj kontenerów Qt.
Jeśli dane są najczęściej używane wewnętrznie w aplikacji i nigdy nie będziesz mógł przenosić się z Qt, a następnie pomijając problemy z wydajnością, użyj kontenerów Qt, ponieważ ułatwi to radzenie sobie z częściami danych, które trafiają do interfejsu użytkownika.
Jeśli dane są używane głównie w połączeniu z innymi bibliotekami, które wiedzą tylko o kontenerach STL, użyj kontenerów STL. Jeśli masz taką sytuację, masz kłopoty bez względu na wszystko, ponieważ będziesz musiał dużo przenosić pomiędzy typami kontenerów, bez względu na to, co robisz.
źródło
Oprócz różnicy COW, kontenery STL są znacznie szerzej obsługiwane na różnych platformach. Qt jest wystarczająco przenośny, jeśli ograniczysz swoją pracę do platform „głównego nurtu”, ale STL jest dostępny również na wielu innych, bardziej niejasnych platformach (np. DSP Texas Instruments).
Ponieważ STL jest standardem, a nie kontrolowaną przez jedną korporację, ogólnie rzecz biorąc, jest więcej programistów, którzy mogą łatwo czytać, rozumieć i modyfikować kod STL oraz więcej zasobów (książek, forów internetowych, konferencji itp.), Aby wspierać je w robiąc to niż w przypadku Qt. Nie oznacza to, że należy unikać Qt tylko z tego powodu; po prostu, wszystkie inne rzeczy są równe, powinieneś domyślnie korzystać z STL, ale oczywiście wszystkie rzeczy są rzadko równe, więc będziesz musiał zdecydować w swoim własnym kontekście, który jest najbardziej sensowny.
W odniesieniu do odpowiedzi AlexKR: wydajność STL jest gwarantowana w określonych granicach, ale dana implementacja może wykorzystywać szczegóły zależne od platformy w celu przyspieszenia ich STL. W tym sensie możesz uzyskiwać różne wyniki na różnych platformach, ale nigdy nie będzie to wolniejsze niż wyraźna gwarancja (błędy modulo).
źródło
Moje pięć centów: kontenery Qt powinny działać podobnie na różnych platformach. Podczas gdy kontenery STL zależą od implementacji STL. Możesz uzyskać różne wyniki wydajności.
EDYCJA: Nie mówię, że STL jest „wolniejszy”, ale wskazuję na efekty różnych szczegółów implementacji.
Sprawdź to , a potem może to .
I to nie jest prawdziwy problem STL. Oczywiście, jeśli masz znaczną różnicę w wydajności, to jest problem w kodzie, który używa STL.
źródło
Myślę, że to zależy od sposobu, w jaki używasz Qt. Jeśli używasz go w całym swoim produkcie, prawdopodobnie lepiej jest używać pojemników Qt. Jeśli zawiera się go tylko (na przykład) część interfejsu użytkownika, może być lepiej użyć standardowych kontenerów C ++.
źródło
Uważam, że STL jest doskonałym oprogramowaniem, ale jeśli mam zrobić programowanie związane z KDE lub Qt, to Qt jest właściwą drogą. Zależy to również od używanego kompilatora, z GCC STL działa całkiem dobrze, jednak jeśli musisz użyć powiedz SUN Studio CC, wówczas STL najprawdopodobniej sprawi ci ból głowy z powodu kompilatora, a nie STL per se. W takim przypadku, ponieważ kompilator sprawi ci ból głowy, po prostu użyj Qt, aby zaoszczędzić ci kłopotów. Tylko moje 2 centy ...
źródło
QVector ma (czasem) duże ograniczenie. Może alokować tylko int bajtów pamięci (pamiętaj, że limit jest w bajtach, a nie w liczbie elementów). Oznacza to, że próba przydzielenia ciągłych bloków pamięci większych niż ~ 2 GB za pomocą QVectora spowoduje awarię. Dzieje się tak z Qt 4 i 5. std :: vector nie ma takich ograniczeń.
źródło
Dla mnie głównym powodem korzystania z kontenerów STL jest to, że potrzebujesz niestandardowego programu przydzielającego, aby ponownie wykorzystać pamięć w bardzo dużych kontenerach. Załóżmy na przykład, że masz QMap, która przechowuje 1000000 pozycji (pary klucz / wartość). W Qt oznacza to dokładnie 1000000 milionów alokacji (
new
połączeń) bez względu na wszystko. W STL zawsze możesz utworzyć niestandardowy alokator, który wewnętrznie przydziela całą tę pamięć jednocześnie i przypisuje ją do każdego wpisu, gdy mapa jest zapełniona.Radzę korzystać z kontenerów STL podczas pisania algorytmów krytycznych pod względem wydajności w logice biznesowej, a następnie przekonwertować je z powrotem na kontenery Qt, gdy wyniki będą gotowe, wyświetlając je za pomocą formantów interfejsu użytkownika i formularzy w razie potrzeby.
źródło
QMapNode<K,V>
dla twojegoK
,V
aby zapewnić swoim własnymoperator new
.