Wahl zwischen vector :: resize () und vector :: Reserve ()

151

Ich ordne meiner vectorMitgliedsvariablen vorab etwas Speicher zu . Der folgende Code ist ein minimaler Teil

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

Jetzt irgendwann, wenn das t_Names.size()gleich ist 1000. Ich beabsichtige, die Größe um zu erhöhen 100. Wenn es dann erreicht 1100, erhöhen Sie erneut um 100und so weiter.

Meine Frage ist, was ich zwischen vector::resize()und wählen soll vector::reserve(). Gibt es eine bessere Wahl für ein solches Szenario?

Edit : Ich habe eine Art genaue Schätzung für die t_Names. Ich schätze , es zu sein um 700zu 800. In bestimmten (seltenen) Situationen kann es jedoch mehr als wachsen 1000.

iammilind
quelle
34
Sie erkennen, dass dies bedeutet, dass das Vektorwachstum nicht mehr konstant amortisiert wird und Sie einen der Leistungsvorteile der Verwendung verlieren std::vector.
Blastfurnace
1
Siehe auch C ++ Made Easier: Wie Vektoren auf der Dr. Dobbs-Website wachsen .
JWW

Antworten:

262

Die beiden Funktionen machen sehr unterschiedliche Dinge!

Das resize() Methode (und die Übergabe des Arguments an den Konstruktor entspricht dieser) fügt eine entsprechende Anzahl von Elementen in den Vektor ein oder löscht ihn, um ihm eine bestimmte Größe zu geben (optional gibt es ein zweites Argument, um ihren Wert anzugeben). size()Dies wirkt sich auf die aus , die Iteration durchläuft alle diese Elemente, push_back wird nach ihnen eingefügt und Sie können direkt mit auf die Elemente zugreifen operator[].

Das reserve() Methode weist nur Speicher zu, lässt ihn jedoch nicht initialisiert. Es wirkt sich nur aus capacity(), size()bleibt aber unverändert. Es gibt keinen Wert für die Objekte, da dem Vektor nichts hinzugefügt wird. Wenn Sie dann die Elemente einfügen, erfolgt keine Neuzuweisung, da dies im Voraus erfolgt ist. Dies ist jedoch der einzige Effekt.

Es kommt also darauf an, was Sie wollen. Wenn Sie ein Array mit 1000 Standardelementen möchten, verwenden Sie resize(). Wenn Sie ein Array möchten, in das Sie voraussichtlich 1000 Elemente einfügen und einige Zuordnungen vermeiden möchten, verwenden Sie reserve().

BEARBEITEN : Durch den Kommentar von Blastfurnace habe ich die Frage erneut gelesen und festgestellt, dass in Ihrem Fall die richtige Antwort nicht manuell vorab zugewiesen wird. Fügen Sie einfach die Elemente am Ende nach Bedarf ein. Der Vektor wird bei Bedarf automatisch neu zugewiesen und führt mehr aus effizienter als die manuelle Art und Weise erwähnt. Der einzige Fall, in dem dies reserve()sinnvoll ist, ist, wenn Sie eine einigermaßen genaue Schätzung der Gesamtgröße haben, die Sie im Voraus benötigen.

EDIT2: Anzeigenfrage bearbeiten: Wenn Sie eine anfängliche Schätzung haben, dann reserve()diese Schätzung. Wenn sich herausstellt, dass dies nicht ausreicht, lassen Sie den Vektor einfach das tun.

Jan Hudec
quelle
Ich habe die Frage bearbeitet. Ich habe eine gewisse Schätzung für die vector.
Iammilind
3
@ Jan: Nun, es ist fragil oder nicht, je nachdem, wie schwierig Sie es sich gemacht haben, die erforderliche Eigenschaft zu erhalten. So etwas 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); }ist ziemlich robust, was die Reservierung des Platzes betrifft. Ich habe keine Ahnung, wie viele Elemente tatsächlich hinzugefügt werden, aber ich habe eine Obergrenze. Im Zweifelsfall ist der Unterschied bei Vektoren, bei denen Sie nur Indizes anstelle von Iteratoren verwenden können, normalerweise vernachlässigbar.
Steve Jessop
4
Ihr Wortlaut ist für jemanden sinnvoll, der bereits die richtige Antwort kennt, kann jedoch leicht Personen irreführen, die die Frage stellen müssen. "resize () ... fügt eine bestimmte Anzahl von Elementen in den Vektor ein" - nur wahr, wenn es zum ersten Mal verwendet wird - es fügt im Allgemeinen die Differenz zwischen der angeforderten Anzahl und der bereits vorhandenen ein size(). "Die Reserve () -Methode weist nur Speicher zu" - sie kann Speicher zuweisen oder nicht, je nachdem, obcapacity() weist bereits ausreicht. Möglicherweise müssen auch Elemente verschoben und der ursprüngliche Speicher freigegeben werden. "Ich möchte ein paar Zuweisungen vermeiden" und Kopien usw.
Tony Delroy
18
Tatsächlich ist das Reservieren vor dem Schieben von entscheidender Bedeutung und muss verwendet werden. Angenommen, Sie codieren eine Art 3D-Modelllader und das Modell hat etwa 15000 Eckpunkte. Wenn Sie versuchen, jeden Scheitelpunkt beim Laden zurückzuschieben, ohne ihn vorher zuzuweisen, dauert dies sehr lange. Ich persönlich habe das erlebt, ich habe versucht, ein Auto-OBJ-Modell mit fast 100000 Eckpunkten zu laden. Es dauerte 30 Sekunden. Dann habe ich den Code mithilfe der Vorzuweisung mit .reserve () überarbeitet, jetzt dauert es 3 Sekunden. Nur eine .reserve (100000) am Anfang des Codes zu setzen, sparte 27 Sekunden.
Deniz
1
@deniz Das ist trivial wahr im Maßstab 100000, aber sehr nicht wahr im Maßstab 100-300, wo Reservierungen verschwenderisch sein können, wenn sie unnötig durchgeführt werden.
Deworde
30

resize()reserviert nicht nur Speicher, sondern erstellt auch so viele Instanzen wie die gewünschte Größe, an die Sie resize()als Argument übergeben. Ordnet aber reserve()nur Speicher zu, es werden keine Instanzen erstellt. Das ist,

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

Ausgabe ( Online-Demo ):

1
1
0
1

Dies ist resize()möglicherweise nicht wünschenswert, wenn Sie die standardmäßig erstellten Objekte nicht möchten. Es wird auch langsam sein. Wenn Sie push_back()neue Elemente hinzufügen, size()erhöht sich außerdem der Vektor weiter, indem neuer Speicher zugewiesen wird (was auch bedeutet, dass die vorhandenen Elemente in den neu zugewiesenen Speicherbereich verschoben werden). Wenn Sie reserve()zu Beginn verwendet haben, um sicherzustellen, dass bereits genügend Speicher zugewiesen ist, wird diesize() erhöht sich der Vektor, wenn Sie darauf push_back()zugreifen. Er weist jedoch erst dann neuen Speicher zu, wenn der für Sie .

Nawaz
quelle
6
Danach reserve(N)können wir operator []harmlos verwenden. richtig ?
Iammilind
2
Während die meisten Implementierungen den genauen Betrag zuweisen, um den Sie anfordern reserve, erfordert die Spezifikation nur, dass mindestens so viel zugewiesen wird, sodass einige Implementierungen bis zu einer bestimmten Grenze aufrunden und somit eine höhere Kapazität als 1000 aufweisen können.
Jan Hudec
16
@iammilind: Nein, wenn der Index größer oder gleich ist v.size(). Beachten Sie, dass reserve(N)sich size()der Vektor nicht ändert .
Nawaz
5
@iammilind: Falsch. Nach dem Aufruf von reSERVE werden keine Einträge hinzugefügt, es wird nur genügend Speicher zum Hinzufügen dieser Einträge abgerufen.
Jan Hudec
2

Aus Ihrer Beschreibung geht hervor, dass Sie den zugewiesenen Speicherplatz des Vektors t_Names "reservieren" möchten.

Beachten Sie, dass resizeder neu zugewiesene Vektor initialisiert wird, der reservenur zugewiesen, aber nicht erstellt wird. Daher ist "Reserve" viel schneller als "Größe ändern"

Informationen zum Unterschied zwischen Größe und Reserve finden Sie in der Dokumentation

tauchen
quelle
1
Bitte beziehen Sie sich stattdessen hier: Vektor und Kapazität ( warum? )
sehe
1
Vielen Dank für die Hinzufügung von Link, sehe
Dip
2

reservieren, wenn die Objekte bei der Reservierung nicht initialisiert werden sollen. Möglicherweise möchten Sie auch die Anzahl der Verwendungszwecke logisch unterscheiden und nachverfolgen, wenn Sie die Größe ändern. Es gibt also einen Verhaltensunterschied in der Benutzeroberfläche: Der Vektor repräsentiert die gleiche Anzahl von Elementen, wenn er reserviert ist, und ist 100 Elemente größer, wenn die Größe in Ihrem Szenario geändert wird.

Gibt es eine bessere Wahl für ein solches Szenario?

Es hängt ganz von Ihren Zielen ab, wenn Sie das Standardverhalten bekämpfen. Einige Leute werden maßgeschneiderte Allokatoren bevorzugen - aber wir brauchen wirklich eine bessere Vorstellung davon, was Sie in Ihrem Programm zu lösen versuchen, um Sie gut zu beraten.

fwiw, viele Vektorimplementierungen verdoppeln einfach die Anzahl der zugewiesenen Elemente, wenn sie wachsen müssen. Versuchen Sie, die Größe der Spitzenzuweisungen zu minimieren, oder versuchen Sie, genügend Speicherplatz für ein sperrenfreies Programm oder etwas anderes zu reservieren?

Justin
quelle
" Reservieren, wenn Sie nicht möchten, dass die Objekte bei der Reservierung initialisiert werden. " Die richtige Formulierung ist, wenn Sie nicht möchten, dass die Objekte existieren . Es ist nicht wie ein nicht initialisiertes Array eines trivial konstruierbaren Typs, bei dem die Objekte nicht gelesen, sondern zugewiesen werden können. Vielmehr ist nur Speicher reserviert, es sind jedoch keine Objekte darin vorhanden, sodass mit operator[]oder auf nichts zugegriffen werden kann.
underscore_d