Od
- oba są ciągłymi kontenerami pamięci;
- pod względem funkcji, deque ma prawie wszystko, co ma wektor, ale więcej, ponieważ bardziej wydajne jest wstawianie z przodu.
Dlaczego Whould ktoś preferuje std::vector
się std::deque
?
Od
Dlaczego Whould ktoś preferuje std::vector
się std::deque
?
Odpowiedzi:
Elementy w a niedeque
są ciągłe w pamięci; elementy są gwarantowane. Więc jeśli potrzebujesz współdziałać ze zwykłą biblioteką C, która potrzebuje ciągłych tablic, lub jeśli zależy Ci (bardzo) na lokalności przestrzennej, możesz preferować . Ponadto, ponieważ istnieje dodatkowa księgowość, inne operacje są prawdopodobnie (nieco) droższe niż ich odpowiedniki . Z drugiej strony, używanie wielu / dużych wystąpień może prowadzić do niepotrzebnej fragmentacji sterty (spowolnienia wywołań ).vector
vector
vector
vector
new
Ponadto, jak wskazano w innym miejscu w StackOverflow , jest więcej dobrej dyskusji tutaj: http://www.gotw.ca/gotw/054.htm .
Aby poznać różnicę, należy wiedzieć, w jaki sposób deque
jest ogólnie wdrażany. Pamięć jest przydzielana w blokach o równych rozmiarach i są one połączone razem (jako tablica lub ewentualnie wektor).
Aby znaleźć n-ty element, należy znaleźć odpowiedni blok, a następnie uzyskać dostęp do elementu w nim zawartego. Jest to stały czas, ponieważ zawsze są to dokładnie 2 wyszukiwania, ale to wciąż więcej niż wektor.
vector
działa również dobrze z interfejsami API, które wymagają ciągłego bufora, ponieważ są to interfejsy API języka C lub są bardziej wszechstronne pod względem możliwości pobierania wskaźnika i długości. (W ten sposób możesz mieć wektor pod spodem lub zwykłą tablicę i wywołać API z bloku pamięci).
Gdzie deque
ma swoje największe zalety to:
Drugi z nich jest mniej znany, ale dla bardzo dużych rozmiarów kolekcji:
Kiedy w przeszłości miałem do czynienia z dużymi kolekcjami i przeniosłem się z modelu ciągłego do modelu blokowego, byliśmy w stanie przechowywać około 5 razy większą kolekcję, zanim skończyła się pamięć w systemie 32-bitowym. Dzieje się tak częściowo dlatego, że podczas ponownego przydzielania w rzeczywistości konieczne było przechowywanie starego bloku, a także nowego, zanim skopiował elementy.
Powiedziawszy to wszystko, możesz mieć kłopoty z std::deque
systemami używającymi „optymistycznej” alokacji pamięci. Chociaż jego próby zażądania dużego rozmiaru bufora w celu ponownej alokacji vector
testamentu prawdopodobnie zostaną w pewnym momencie odrzucone przez a bad_alloc
, optymistyczny charakter osoby dokonującej alokacji prawdopodobnie zawsze spełni żądanie o mniejszy bufor, o który wnioskuje deque
a, co prawdopodobnie spowoduje system operacyjny, aby zabić proces, aby spróbować zdobyć trochę pamięci. Ktokolwiek wybierze, może nie być zbyt przyjemny.
Rozwiązaniem w takim przypadku jest ustawienie flag na poziomie systemu, aby zastąpić optymistyczną alokację (nie zawsze wykonalne) lub nieco bardziej ręczne zarządzanie pamięcią, np. Za pomocą własnego alokatora sprawdzającego użycie pamięci lub podobnych. Oczywiście nie idealny. (Co może odpowiedzieć na twoje pytanie, jak preferować wektor ...)
Wielokrotnie zaimplementowałem zarówno wektor, jak i deque. deque jest znacznie bardziej skomplikowany z punktu widzenia implementacji. Ta komplikacja przekłada się na więcej kodu i bardziej złożony kod. Więc zazwyczaj zobaczysz trafienie rozmiaru kodu, gdy wybierzesz deque zamiast wektora. Możesz również doświadczyć niewielkiego spadku szybkości, jeśli twój kod używa tylko rzeczy, w których wektor się wyróżnia (tj. Push_back).
Jeśli potrzebujesz podwójnej kolejki, deque jest zdecydowanym zwycięzcą. Ale jeśli robisz większość swoich wstawek i wymazań z tyłu, wektor będzie wyraźnym zwycięzcą. Jeśli nie masz pewności, zadeklaruj swój kontener za pomocą typedef (aby łatwo było przełączać się tam iz powrotem) i dokonaj pomiaru.
vector
.) Napisałem implementację, do której link znajduje się poniżej w mojej odpowiedzi . Może być równie szybki, vector
ale ma znacznie szersze zastosowanie (np. Podczas tworzenia szybkiej kolejki).
std::deque
nie ma gwarantowanej pamięci ciągłej - i często jest nieco wolniejszy w przypadku dostępu indeksowanego. Deque jest zwykle implementowany jako „lista wektorów”.
Według http://www.cplusplus.com/reference/stl/deque/ , „w przeciwieństwie do wektorów, deques nie mają gwarancji posiadania wszystkich swoich elementów w ciągłych lokalizacjach pamięci, co eliminuje możliwość bezpiecznego dostępu za pomocą arytmetyki wskaźnikowej”.
Deques są nieco bardziej skomplikowane, po części dlatego, że niekoniecznie mają ciągły układ pamięci. Jeśli potrzebujesz tej funkcji, nie powinieneś używać deque.
(Wcześniej moja odpowiedź wskazywała na brak standaryzacji (z tego samego źródła, co powyżej, „deques mogą być implementowane przez określone biblioteki na różne sposoby”), ale w rzeczywistości dotyczy to prawie każdego standardowego typu danych bibliotecznych.)
std::deque
jest nie mniej znormalizowany niż std::vector
. Nie sądzę, aby wymagania dotyczące złożoności std::deque
można było spełnić dzięki ciągłemu przechowywaniu.
deque
nie można spełnić w przypadku ciągłego przechowywania?
deque
, a mianowicie, że wstawienie na końcach nie unieważnia odniesienia do istniejących elementów. To wymaganie oznacza nieciągłą pamięć.
Myślę, że to dobry pomysł, aby wykonać test wydajności w każdym przypadku. I podejmij decyzję opierając się na tych testach.
Wolałbym std::deque
niż std::vector
w większości przypadków.
vector
. Możemy wywnioskować, że dlaczego nie jest to następstwem. Stwierdzenie, że wolisz deque
z nieznanych powodów od nieokreślonych testów, nie jest odpowiedzią.
Nie wolałbyś, aby wektor był deque zgodnie z tymi wynikami testu (ze źródłem).
Oczywiście powinieneś przetestować swoją aplikację / środowisko, ale podsumowując:
Z jednej strony wektor jest dość często po prostu szybszy niż deque. Jeśli w rzeczywistości nie potrzebujesz wszystkich funkcji deque, użyj wektora.
Z drugiej strony, czasami to robisz funkcje, które potrzebują wektor nie daje, w takim przypadku należy użyć deque. Na przykład wzywam każdego, aby spróbował przepisać ten kod bez użycia deque i bez ogromnej zmiany algorytmu.
push_back
i pop_back
operacji, deque<int>
zawsze jest co najmniej 20% szybciej niż vector<int>
w moich testów (GCC z O3). Myślę, że dlatego deque
jest to standardowy wybór w przypadku std::stack
...
Zauważ, że pamięć wektorowa jest ponownie alokowana, gdy tablica rośnie. Jeśli masz wskaźniki do elementów wektorowych, staną się nieprawidłowe.
Ponadto, jeśli usuniesz element, iteratory staną się nieprawidłowe (ale nie „for (auto ...)”).
Edycja: zmieniono „deque” na „vector”
std::deque
ma bardzo mały maksymalny rozmiar bloku (~ 16 bajtów, jeśli dobrze pamiętam; może 32) i jako taka nie działa zbyt dobrze dla realistycznych aplikacji.deque<T>
Gdziesizeof(T) > 8
(lub 16? To mała liczba) ma o tych samych charakterystyk, jakovector<T*>
, gdzie każdy element jest przydzielane dynamicznie. Inne implementacje mają różne maksymalne rozmiary bloków, więc pisanie kodu, który ma stosunkowo takie same parametry wydajności na różnych platformach, jest trudnedeque
.