Wybór między vector :: resize () i vector :: Reserve ()


151

Wstępnie przydzielam trochę pamięci do mojej vectorzmiennej składowej. Poniższy kod to minimalna część

class A {
  vector<string> t_Names;
public:
  A () : t_Names(1000) {}
};

Teraz w pewnym momencie, jeśli t_Names.size()równa się 1000. Zamierzam zwiększyć rozmiar o 100. Następnie, jeśli osiągnie 1100, ponownie zwiększ o 100i tak dalej.

Moje pytanie brzmi, co wybrać między vector::resize()a vector::reserve(). Czy jest lepszy wybór w takim scenariuszu?

Edycja : mam dokładne oszacowanie dla t_Names. Szacuję, że jest ok 700do 800. Jednak w niektórych (rzadkich) sytuacjach może wzrosnąć bardziej niż 1000.


34
Zdajesz sobie sprawę, że robienie tego oznacza, że ​​wzrost wektora nie jest już amortyzowany w stałym czasie i tracisz jedną z korzyści wydajnościowych wynikających z używania std::vector.
Blastfurnace,

1
Powiązane, zobacz C ++ stało się łatwiejsze: jak wektory rosną w witrynie Dr. Dobbs.
jww

Odpowiedzi:


262

Te dwie funkcje robią bardzo różne rzeczy!

resize()Metoda (i przechodzącej argumentu konstruktora jest równoważna) będzie wstawić lub usunąć odpowiednią liczbę elementów do wektora, aby podane wielkości (ma opcjonalny drugi argument do określenia ich wartości). Wpłynie to na size(), iteracja przejdzie przez wszystkie te elementy, push_back wstawi po nich i możesz uzyskać do nich bezpośredni dostęp za pomocą operator[].

reserve()Metoda przydziela tylko pamięć, ale pozostawia niezainicjowanymi. Ma to tylko wpływ capacity(), ale size()pozostanie niezmienione. Nie ma wartości dla obiektów, ponieważ nic nie jest dodawane do wektora. Jeśli następnie wstawisz elementy, ponowna alokacja nie nastąpi, ponieważ została wykonana z góry, ale to jedyny efekt.

Więc to zależy od tego, czego chcesz. Jeśli chcesz mieć tablicę 1000 domyślnych elementów, użyj resize(). Jeśli chcesz mieć tablicę, do której chcesz wstawić 1000 elementów i chcesz uniknąć kilku alokacji, użyj reserve().

EDYCJA: Komentarz Blastfurnace sprawił, że ponownie przeczytałem pytanie i zdałem sobie sprawę, że w twoim przypadku poprawna odpowiedź to nie przydzielaj wstępnie ręcznie. Po prostu wkładaj elementy na końcu, tak jak potrzebujesz. Wektor będzie automatycznie przydzielić według potrzeb i uczyni go bardziej efektywnie niż ręczny sposób wymienić. Jedynym przypadkiem, w którym reserve()ma to sens, jest dość precyzyjne oszacowanie całkowitego rozmiaru, którego potrzebujesz, łatwo dostępne z wyprzedzeniem.

EDIT2: Edycja pytania reklamowego: jeśli masz wstępne oszacowanie, to reserve()oszacowanie. Jeśli okaże się to niewystarczające, po prostu pozwól wektorowi zrobić swoje.


Redagowałem pytanie. Mam pewne szacunki dotyczące vector.
iammilind,

3
@Jan: cóż, jest kruchy lub nie, w zależności od tego, jak trudne jest dla siebie utrzymanie wymaganej nieruchomości. Coś takiego x.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }jest dość solidne, jeśli chodzi o rezerwowanie miejsca. Nie mam pojęcia, ile elementów faktycznie zostanie dodanych, ale mam górną granicę. Oczywiście, gdy masz wątpliwości, w przypadku wektorów możesz po prostu użyć indeksów zamiast iteratorów, różnica jest zwykle nieistotna.
Steve Jessop,

4
Twoje sformułowanie ma sens dla kogoś, kto już zna prawidłową odpowiedź, ale może łatwo wprowadzić w błąd osoby, które chcą zadać pytanie. „resize ()… wstawi określoną liczbę elementów do wektora” - prawdziwe tylko przy pierwszym użyciu - zazwyczaj wstawia różnicę między żądaną liczbą a istniejącą size(). „Metoda rezerwacji () tylko przydziela pamięć” - może, ale nie capacity()musi, przydzielać pamięć w zależności od tego, czy jest już wystarczająca, może również wymagać przeniesienia elementów i zwolnienia ich pierwotnej pamięci. „chcę uniknąć kilku przydziałów” i kopii itp.
Tony Delroy,

19
Właściwie zarezerwowanie miejsca przed pchaniem jest niezbędne i należy je wykorzystać. Załóżmy, że kodujesz jakiś rodzaj programu ładującego model 3D, a model ma około 15000 wierzchołków. Jeśli spróbujesz odepchnąć każdy wierzchołek podczas ładowania bez wcześniejszego przydzielenia ich, zajmie to dużo czasu. Osobiście tego doświadczyłem, próbowałem załadować model samochodu .obj z blisko 100000 wierzchołków, zajęło to 30 sekund. Następnie refaktoryzowałem kod przy użyciu wstępnej alokacji z .reserve (), teraz zajmuje to 3 sekundy. Samo umieszczenie .reserve (100000) na początku kodu pozwoliło zaoszczędzić 27 sekund.
deniz

1
@deniz To banalna prawda w skali 100000, ale bardzo nieprawda w skali 100-300, gdzie rezerwowanie może być marnotrawstwem, jeśli jest wykonywane niepotrzebnie.
deworde

30

resize()nie tylko alokuje pamięć, ale także tworzy tyle instancji, ile żądany rozmiar, który przekazujesz resize()jako argument. Ale reserve()tylko przydziela pamięć, a nie tworzy instancji. To jest,

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

Wyjście ( demo online ):

1
1
0
1

Więc resize()może nie być pożądane, jeśli nie chcesz domyślnie tworzonych obiektów. Będzie to również powolne. Poza tym, jeśli push_back()dodasz do niego nowe elementy, size()wektor będzie się dalej zwiększał poprzez przydzielanie nowej pamięci (co oznacza również przenoszenie istniejących elementów do nowo przydzielonej przestrzeni pamięci). Jeśli użyłeś reserve()na początku, aby upewnić się, że jest już wystarczająca ilość przydzielonej pamięci, size()wektor zwiększy się, gdy push_back()do niego przejdziesz , ale nie przydzieli nowej pamięci ponownie, dopóki nie zabraknie miejsca, które zostało dla niego zarezerwowane .


6
Po wykonaniu tej czynności reserve(N)możemy używać operator []nieszkodliwie. prawda?
iammilind,

2
Podczas gdy większość implementacji przydzieli dokładną ilość, o którą prosisz reserve, specyfikacja wymaga, aby alokowała co najmniej tyle, więc niektóre implementacje mogą zaokrąglić w górę do pewnej granicy, a tym samym pokazać większą pojemność niż 1000.
Jan Hudec

16
@iammilind: Nie, jeśli indeks jest większy lub równy v.size(). Zauważ, że reserve(N)nie zmienia to size()wektora.
Nawaz,

5
@iammilind: INcorrect. Po wywołaniu funkcji reSERVE żadne wpisy nie są dodawane, uzyskuje się jedynie pamięć do ich dodania.
Jan Hudec,

2

Z twojego opisu wynika, że ​​chcesz "zarezerwować" przydzieloną przestrzeń pamięci wektorowej t_Names.

Zwróć uwagę, że resizezainicjuj nowo przydzielony wektor, w którym reservepo prostu alokuje, ale nie konstruuje. Dlatego „rezerwa” jest znacznie szybsza niż „zmiana rozmiaru”

Możesz zapoznać się z dokumentacją dotyczącą różnicy między zmianą rozmiaru a rezerwą


1
Proszę odnieść się tutaj zamiast: wektor i pojemności ( dlaczego? )
sehe

1
Dzięki za dodanie linku, zobacz
dip

2

zarezerwuj, gdy nie chcesz, aby obiekty były inicjowane po zarezerwowaniu. możesz też chcieć logicznie różnicować i śledzić jego liczbę w porównaniu z liczbą użycia podczas zmiany rozmiaru. więc istnieje różnica w zachowaniu w interfejsie - wektor będzie reprezentował tę samą liczbę elementów po zarezerwowaniu, a po zmianie rozmiaru w scenariuszu będzie o 100 elementów większy.

Czy jest lepszy wybór w takim scenariuszu?

zależy to całkowicie od twoich celów podczas walki z domyślnym zachowaniem. niektórzy wolą niestandardowe podzielniki - ale naprawdę potrzebujemy lepszego wyobrażenia o tym, co próbujesz rozwiązać w swoim programie, aby dobrze Ci doradzić.

fwiw, wiele implementacji wektorowych po prostu podwoi liczbę przydzielonych elementów, gdy będą musiały wzrosnąć - czy próbujesz zminimalizować maksymalne rozmiary alokacji, czy próbujesz zarezerwować wystarczającą ilość miejsca dla jakiegoś programu bez blokad lub czegoś innego?


zarezerwuj, gdy nie chcesz, aby obiekty były inicjowane po zarezerwowaniu. ” Prawidłowe sformułowanie to sytuacja, w której nie chcesz, aby obiekty istniały . To nie jest jak niezainicjowana tablica typu banalnie konstruowalnego, w której obiektów nie można odczytać, ale można je przypisać; raczej tylko pamięć jest zarezerwowana, ale nie istnieją w niej żadne obiekty, więc nie można uzyskać do nich dostępu za pomocą operator[]ani niczego.
underscore_d
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.