Czytałem o kontenerach STL w mojej książce o C ++, a konkretnie w sekcji o STL i jego kontenerach. Teraz rozumiem, że każda z nich ma swoje specyficzne właściwości i jestem blisko zapamiętywania ich wszystkich ... Ale nie rozumiem jeszcze, w którym scenariuszu każdy z nich jest używany.
Jakie jest wyjaśnienie? Przykładowy kod jest znacznie preferowany.
c++
stl
container-data-type
Daniel Sloof
źródło
źródło
Odpowiedzi:
Ta ściągawka stanowi całkiem dobre podsumowanie różnych pojemników.
Zobacz schemat blokowy na dole, jako przewodnik do użycia w różnych scenariuszach użycia:
Stworzony przez Davida Moore'a i licencjonowany CC BY-SA 3.0
źródło
vector
raczej niż pusty. stackoverflow.com/questions/10699265/…unordered_map
iunordered_set
(i ich wiele wariantów), których nie ma na schemacie, ale są dobrym wyborem, gdy nie dbasz o porządek, ale musisz znaleźć elementy według klucza. Ich wyszukiwanie to zwykle O (1) zamiast O (log n).Oto schemat blokowy zainspirowany wersją Davida Moore'a (patrz wyżej), która jest aktualna (głównie) z nowym standardem (C ++ 11). To tylko moje osobiste podejście do sprawy, nie jest to bezdyskusyjne, ale pomyślałem, że może być cenne w tej dyskusji:
źródło
vector (sorted)
jest trochę niespójny z resztą. To nie jest inny typ kontenera, tylko ten sam,std::vector
ale posortowany. Co ważniejsze, nie rozumiem, dlaczego nie można użyćstd::set
polecenia dla uporządkowanej iteracji, jeśli jest to standardowe zachowanie iteracji przez zestaw. Jasne, jeśli odpowiedź mówi o uporządkowanym dostępie do wartości koryta kontenera[]
, to ok, możesz to zrobić tylko z sotowanymstd::vector
. Ale w obu przypadkach decyzję należy podjąć zaraz po pytaniu „potrzebne zamówienie”Prosta odpowiedź: używaj
std::vector
do wszystkiego, chyba że masz prawdziwy powód, aby zrobić inaczej.Kiedy znajdziesz przypadek, w którym myślisz: „Ojej,
std::vector
tutaj nie działa dobrze z powodu X”, idź na podstawie X.źródło
std::remove_if
prawie zawsze przewyższa podejście „usuń podczas iteracji”.Spójrz na Effective STL autorstwa Scotta Meyersa. Jest dobry w wyjaśnianiu, jak używać STL.
Jeśli chcesz przechowywać określoną / nieokreśloną liczbę obiektów i nigdy nie zamierzasz ich usuwać, to wektor jest tym, czego chcesz. Jest to domyślny zamiennik tablicy C i działa jak jeden, ale nie przepełnia się. Możesz również wcześniej ustawić jego rozmiar za pomocązerawienia ().
Jeśli chcesz przechowywać nieokreśloną liczbę obiektów, ale będziesz je dodawać i usuwać, prawdopodobnie chcesz mieć listę ... ponieważ możesz usunąć element bez przenoszenia kolejnych elementów - w przeciwieństwie do wektora. Jednak zajmuje więcej pamięci niż wektor i nie można uzyskać sekwencyjnego dostępu do elementu.
Jeśli chcesz wziąć kilka elementów i znaleźć tylko unikalne wartości tych elementów, wczyta je wszystkie w zestaw, a także posortuje je dla ciebie.
Jeśli masz wiele par klucz-wartość i chcesz je posortować według klucza, mapa jest przydatna ... ale będzie zawierała tylko jedną wartość na klucz. Jeśli potrzebujesz więcej niż jednej wartości na klucz, możesz mieć wektor / listę jako wartość na mapie lub użyć multimapy.
Nie ma go w STL, ale jest w aktualizacji TR1 do STL: jeśli masz wiele par klucz-wartość, które będziesz sprawdzać według klucza, a nie obchodzi ich kolejność, możesz chcę użyć skrótu - który jest tr1 :: unordered_map. Użyłem go z Visual C ++ 7.1, gdzie nazywał się stdext :: hash_map. Ma odnośnik O (1) zamiast odnośnika O (log n) dla mapy.
źródło
hash_map
nie jest bardzo dobrą implementacją. Mam nadzieję, że poradzili sobie lepiejunordered_map
.list
robi. Raczej rażący błąd.Przeprojektowałem schemat blokowy, aby miał 3 właściwości:
Schemat blokowy:
Więcej informacji pod tym linkiem .
źródło
Ważnym punktem tylko krótko wspomniano dotąd, jest to, że jeśli wymagają pamięci ciągłej (jak tablica C daje), a następnie można użyć tylko
vector
,array
albostring
.Użyj,
array
jeśli rozmiar jest znany w czasie kompilacji.Użyj,
string
jeśli potrzebujesz pracować tylko z typami znaków i potrzebujesz łańcucha, a nie tylko kontenera ogólnego przeznaczenia.Użyj
vector
we wszystkich innych przypadkach (i takvector
powinien być domyślnym wyborem kontenera).Za pomocą wszystkich tych trzech elementów można użyć
data()
funkcji członka, aby uzyskać wskaźnik do pierwszego elementu kontenera.źródło
Wszystko zależy od tego, co chcesz przechowywać i co chcesz zrobić z pojemnikiem. Oto kilka (bardzo niewyczerpujących) przykładów klas kontenerów, których najczęściej używam:
vector
: Kompaktowy układ z niewielkim lub zerowym obciążeniem pamięci dla każdego zawartego obiektu. Wydajny w iteracji. Dołączanie, wstawianie i usuwanie może być kosztowne, szczególnie w przypadku złożonych obiektów. Tanie znaleźć obiekt zawarty w indeksie, np. MyVector [10]. Użyj tam, gdzie użyłbyś tablicy w C. Dobrze, gdzie masz wiele prostych obiektów (np. Int). Nie zapomnij użyćreserve()
przed dodaniem wielu przedmiotów do kontenera.list
: Mały narzut pamięci na zawarty obiekt. Wydajny w iteracji. Dołączanie, wstawianie i usuwanie są tanie. Użyj tam, gdzie użyłbyś połączonej listy w C.set
(imultiset
): Znaczny narzut pamięci na zawarty obiekt. Użyj tam, gdzie musisz szybko dowiedzieć się, czy ten kontener zawiera dany obiekt, lub wydajnie scalaj kontenery.map
(imultimap
): Znaczny narzut pamięci na zawarty obiekt. Użyj tam, gdzie chcesz przechowywać pary klucz-wartość i szybko wyszukiwać wartości według klucza.Schemat blokowy na ściągawki sugerowany przez zdan zapewnia bardziej wyczerpujący przewodnik.
źródło
Jedna lekcja, której się nauczyłem, to: spróbuj zawinąć w klasę, ponieważ zmiana rodzaju pojemnika jednego dobrego dnia może przynieść duże niespodzianki.
Nie kosztuje dużo z góry i oszczędza czas podczas debugowania, gdy chcesz przerwać za każdym razem, gdy ktoś wykonuje operację x na tej strukturze.
Jeśli chodzi o wybranie idealnej struktury danych dla zadania:
Każda struktura danych zapewnia pewne operacje, które mogą mieć różną złożoność czasową:
O (1), O (lg N), O (N) itp.
Zasadniczo musisz zgadnąć, które operacje zostaną wykonane najbardziej, i użyć struktury danych, która ma tę operację jako O (1).
Proste, prawda? (-:
źródło
auto myIterator = whateverCollection.begin(); // <-- immune to changes of container type
typedef Collection<Foo*> CollectionOfFoo;
wystarczyłoby?Rozszerzyłem fantastyczny schemat blokowy Mikaela Perssona . Dodałem kilka kategorii kontenerów, kontener tablicowy i kilka notatek. Jeśli chcesz mieć własną kopię, oto rysunek Google. Dzięki, Mikael za wykonanie prac przygotowawczych! Selektor kontenerów w C ++
źródło
Odpowiedziałem na to w innym pytaniu, które jest oznaczone jako duplikat tego. Uważam jednak, że miło jest odwołać się do dobrych artykułów na temat wyboru standardowego pojemnika.
Jak odpowiedział @David Thornley, std :: vector to droga, jeśli nie ma innych specjalnych potrzeb. Jest to rada udzielona przez twórcę C ++, Bjarne Stroustrup na blogu w 2014 roku.
Oto link do artykułu https://isocpp.org/blog/2014/06/stroustrup-lists
i cytat z tego,
W komentarzach użytkownik @NathanOliver zapewnia również kolejny dobry blog, który zawiera bardziej konkretne pomiary. https://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vector-list-deque.html .
źródło