Was ist das capacity()
von einem, std::vector
das mit dem Standardkonstruktor erstellt wird? Ich weiß, dass das size()
Null ist. Können wir feststellen, dass ein standardmäßig konstruierter Vektor keine Heap-Speicherzuordnung aufruft?
Auf diese Weise wäre es möglich, ein Array mit einer beliebigen Reserve unter Verwendung einer einzelnen Zuordnung zu erstellen, wie z std::vector<int> iv; iv.reserve(2345);
. Nehmen wir an, ich möchte aus irgendeinem Grund die size()
2345 nicht starten .
Zum Beispiel unter Linux (g ++ 4.4.5, Kernel 2.6.32 amd64)
#include <iostream>
#include <vector>
int main()
{
using namespace std;
cout << vector<int>().capacity() << "," << vector<int>(10).capacity() << endl;
return 0;
}
gedruckt 0,10
. Ist es eine Regel oder ist es vom STL-Anbieter abhängig?
c++
memory-management
stl
vector
Notinlist
quelle
quelle
swap
bleiben alle Iteratoren und Referenzen gültig (außerend()
s). Das bedeutet, dass ein Inline-Puffer nicht möglich ist.Antworten:
Der Standard gibt nicht an, wie die Initiale
capacity
eines Containers lauten soll, daher verlassen Sie sich auf die Implementierung. Bei einer gemeinsamen Implementierung wird die Kapazität bei Null gestartet, es gibt jedoch keine Garantie. Auf der anderen Seite gibt es keine Möglichkeit, Ihre Strategie zustd::vector<int> iv; iv.reserve(2345);
verbessern, sich daran zu halten.quelle
vector::reserve
ist jedoch nicht dasselbe wie die Angabe einer Anfangsgröße. Die Vektorkonstruktoren, die einen anfänglichen Größenwert / eine Kopie annehmen, initialisierenn
Objekte und weisen daher eine lineare Komplexität auf. OTOH bedeutet das Aufrufen von Reserve nur das Kopieren / Verschieben vonsize()
Elementen, wenn eine Neuzuweisung ausgelöst wird. Auf einem leeren Vektor gibt es nichts zu kopieren. Letzteres kann daher auch dann wünschenswert sein, wenn die Implementierung Speicher für einen standardmäßig konstruierten Vektor zuweist.Die Speicherimplementierungen von std :: vector variieren erheblich, aber alle, auf die ich gestoßen bin, beginnen bei 0.
Der folgende Code:
Gibt die folgende Ausgabe:
unter GCC 5.1 und:
unter MSVC 2013.
quelle
Soweit ich den Standard verstanden habe (obwohl ich eigentlich keine Referenz nennen konnte), wurden die Instanziierung von Containern und die Speicherzuweisung aus gutem Grund absichtlich entkoppelt. Dafür haben Sie unterschiedliche, separate Anrufe für
constructor
um den Container selbst zu erstellenreserve()
einen entsprechend großen Speicherblock vorab zuzuweisen, um mindestens (!) eine gegebene Anzahl von Objekten aufzunehmenUnd das macht sehr viel Sinn. Das einzige Existenzrecht
reserve()
besteht darin, Ihnen die Möglichkeit zu geben, beim Wachsen des Vektors möglicherweise teure Neuzuweisungen zu codieren. Um nützlich zu sein, müssen Sie die Anzahl der zu speichernden Objekte kennen oder zumindest eine fundierte Vermutung anstellen können. Wenn dies nicht gegeben ist, halten Sie sich besser fern,reserve()
da Sie nur die Neuzuweisung für verschwendeten Speicher ändern.Also alles zusammen:
reserve()
und dieser muss sich nicht am selben Bauort befinden (könnte / sollte natürlich später sein, nachdem Sie die erforderliche Größe für die Unterbringung erkannt haben).reserve()
, würde dies den beabsichtigten Job vereiteln , nicht wahr ?push_back()
- sofern dies nicht bereits zuvor explizit zugewiesen wurdereserve()
.All dies kommt nur dann zum vollen Betrieb und zum Vorteil, wenn es nicht durch einen zuweisenden Konstruktor gestört wird. Sie haben angemessene Standardeinstellungen für gängige Szenarien, die bei Bedarf von
reserve()
(undshrink_to_fit()
) überschrieben werden können . Selbst wenn der Standard dies nicht explizit angibt, bin ich mir ziemlich sicher, dass die Annahme, dass ein neu konstruierter Vektor nicht vorbelegt wird, eine ziemlich sichere Wette für alle aktuellen Implementierungen ist.quelle
Als kleine Ergänzung zu den anderen Antworten stellte ich fest, dass beim Ausführen unter Debug-Bedingungen mit Visual Studio ein standardmäßig erstellter Vektor dem Heap weiterhin zugewiesen wird, obwohl die Kapazität bei Null beginnt.
Insbesondere wenn _ITERATOR_DEBUG_LEVEL! = 0 ist, weist der Vektor etwas Platz zu, um bei der Iteratorprüfung zu helfen.
https://docs.microsoft.com/en-gb/cpp/standard-library/iterator-debug-level
Ich fand das nur etwas ärgerlich, da ich zu diesem Zeitpunkt einen benutzerdefinierten Allokator verwendete und nicht mit der zusätzlichen Allokation rechnete.
quelle
Dies ist eine alte Frage, und alle Antworten hier haben den Standpunkt des Standards und die Art und Weise, wie Sie eine anfängliche Kapazität auf tragbare Weise erhalten können, zu Recht erklärt
std::vector::reserve
.Ich werde jedoch erklären, warum es für eine STL-Implementierung nicht sinnvoll ist, beim Erstellen eines
std::vector<T>
Objekts Speicher zuzuweisen .std::vector<T>
von unvollständigen Typen;Vor C ++ 17 war es ein undefiniertes Verhalten, ein zu erstellen,
std::vector<T>
wenn die Definition von zumT
Zeitpunkt der Instanziierung noch unbekannt ist. Diese Einschränkung wurde jedoch in C ++ 17 gelockert .Um Speicher für ein Objekt effizient zuzuweisen, müssen Sie dessen Größe kennen. Ab C ++ 17 und darüber hinaus haben Ihre Clients möglicherweise Fälle, in denen Ihre
std::vector<T>
Klasse die Größe von nicht kenntT
. Ist es sinnvoll, Speicherzuordnungsmerkmale abhängig von der Typvollständigkeit zu haben?Unwanted Memory allocations
Es gibt viele, viele, viele Male, in denen Sie ein Diagramm in der Software modellieren müssen. (Ein Baum ist ein Graph); Sie werden es höchstwahrscheinlich wie folgt modellieren:
Denken Sie jetzt einen Moment nach und stellen Sie sich vor, Sie hätten viele Endknoten. Sie wären sehr sauer, wenn Ihre STL-Implementierung zusätzlichen Speicherplatz zuweist, nur um Objekte zu haben
children
.Dies ist nur ein Beispiel, denken Sie an mehr ...
quelle
Standard gibt keinen Anfangswert für die Kapazität an, aber der STL-Container nimmt automatisch zu, um so viele Daten aufzunehmen, wie Sie eingegeben haben, vorausgesetzt, Sie überschreiten nicht die maximale Größe (verwenden Sie die Mitgliedsfunktion max_size, um dies zu wissen). Bei Vektoren und Strings wird das Wachstum von Realloc verwaltet, wenn mehr Platz benötigt wird. Angenommen, Sie möchten einen Vektor mit dem Wert 1-1000 erstellen. Ohne Verwendung von Reserve führt der Code in der Regel zu 2 bis 18 Neuzuweisungen während der folgenden Schleife:
Das Ändern des Codes zur Verwendung von Reserve kann zu 0 Zuweisungen während der Schleife führen:
Grob gesagt wachsen die Vektor- und String-Kapazitäten jedes Mal um den Faktor 1,5 bis 2.
quelle