Wie wird push_back im STL-Vektor implementiert?

8

Ich wurde diese Frage in einem Interview gestellt.

Die Punkte, die ich beantwortet habe, sind so.

1) ein Index, der auf die aktuelle Position zeigt;

2) falls nötig, die Größe ändern.

Kann jemand mehr ausarbeiten?

    
skydoor 12.04.2010, 20:04
quelle

9 Antworten

20

Eine STL vector hat eine size (aktuelle Anzahl gespeicherter Elemente) und ein capacity (derzeit zugewiesener Speicherplatz).

  • Wenn size < capacity , ein push_back einfach das neue Element an das Ende setzt und die size um 1 erhöht.
  • Wenn size == capacity vor push_back , ein neues, größeres Array zugewiesen wird (doppelt so groß wie gewöhnlich, aber dies ist ein implementierungsabhängiges afaik), werden alle aktuellen Daten kopiert (einschließlich des neuen Elements) und der alte zugewiesene Speicherplatz wird freigegeben. Dies kann eine Ausnahme auslösen, wenn die Zuweisung fehlschlägt.

Die Komplexität der Operation ist amortized O (1), was bedeutet, dass während einer push_back , die eine Größenänderung verursacht, keine Operation mit konstanter Zeit durchgeführt wird (sondern im Allgemeinen über viele) Operationen, ist es).

    
tzaman 12.04.2010 20:13
quelle
4
%Vor%     
sbi 12.04.2010 20:30
quelle
3

Das ist natürlich inhärent Implementierung definiert. Angenommen, es geht darum, wie jemand ein dynamisches Array implementieren würde, wäre es im Allgemeinen so:

  1. push_back überprüft capacity() und stellt sicher, dass es mindestens eins größer ist als size() .
  2. Wenn für das neue Element keine Kapazität vorhanden ist, weist der Vektor seinen gesamten zugrunde liegenden Speicher neu zu und kopiert den Inhalt des alten Speichers in den neuen Speicher. Der alte Speicher wird freigegeben.
  3. Das neue Element wird an das Ende des dynamischen Arrays kopiert.

Einige STL-Implementierungen werden einige der Kopien mithilfe von Swaps (d. h. für Containern von Containern) bereitstellen, aber in den meisten Fällen funktioniert das genau so.

    
Billy ONeal 12.04.2010 20:13
quelle
1

Möglicherweise haben sie gesucht, dass push_back eine Kopie des Objekts erstellt, das auf das vector (unter Verwendung seines Kopierkonstruktors) geschoben wird.

Bezüglich der Größenanpassung: Der Standard sagt a.push_back(x) entspricht a.insert(a.end(),x) . Die Definition von insert sagt zum Teil: "Verursacht Neuzuweisung, wenn die neue Größe größer als die alte Kapazität ist."

Der Standard sagt was die Funktionen tun sollen. Aber wie sie implementiert werden, ist in den meisten Fällen implementierungsspezifisch.

    
Nate 12.04.2010 20:12
quelle
0

vector verwendet keine verknüpfte Liste. Es verwendet kontinuierlichen Speicher.

Wenn nicht genügend reservierter Speicherplatz vorhanden ist, reserviert push_back einen neuen Speicherbereich zweimal so groß wie die ursprüngliche vector . Dadurch ist die amortisierte Laufzeit O (1).

    
Axel Gneiting 12.04.2010 20:13
quelle
0

Dank einiger Kommentare überarbeite ich eine sehr falsche ursprüngliche Antwort vollständig.

Laut STL-Spezifikation war Ihre Antwort korrekt. Der Vektor wird als dynamisch skaliertes Array implementiert:

  

Vektorcontainer sind als dynamische Arrays implementiert; Genauso wie reguläre Arrays haben Vektorcontainer ihre Elemente an zusammenhängenden Speicherorten gespeichert, was bedeutet, dass auf ihre Elemente nicht nur mit Iteratoren zugegriffen werden kann, sondern auch Offsets für reguläre Zeiger auf Elemente verwendet werden können   Im Gegensatz zu regulären Arrays wird der Speicher in Vektoren jedoch automatisch verarbeitet, sodass er bei Bedarf erweitert und verkleinert werden kann.

    
Heath Hunnicutt 12.04.2010 20:15
quelle
0

Wie viel Detail wollte der Interviewer? Hat er zum Beispiel nach dir gesucht, um in die Details auf der unteren Ebene zu gelangen?

Neben der üblichen Größenanpassung, die erforderlich ist, um die durchschnittliche Semantik von O (1) beizubehalten, sind einige Dinge zu berücksichtigen, die aber nicht darauf beschränkt sind:

  • Ausnahmesicherheit : Bietet die Implementierung eine Garantie, dass der Status des Vektors nicht geändert wird, wenn beim Anhängen des neuen Elements eine Ausnahme ausgelöst wird (z. B. Rollback-Semantik)? Zum Beispiel könnte eine Ausnahme beim Zuordnen, Kopieren, Einfügen usw. ausgelöst werden.
  • Richtige Verwendung des Zuweisers : Verwendet die Implementierung korrekt den Zuweiser vector anstelle des einfachen alten Zuweisers new (beide können identisch sein oder nicht)? Im Idealfall wird dies transparent durch den% resize-Code von vector gehandhabt, Implementierungen könnten sich jedoch durchaus unterscheiden.
Void 12.04.2010 20:30
quelle
0

Wie so:

%Vor%     
Crazy Eddie 12.04.2010 20:45
quelle
0

Wenn Größe & lt; capasity, dann scheint alles in Ordnung zu sein, du fügst einfach en element in vector ein, die Komplexität ergibt sich, wenn size == capasity.

Es ist leicht zu erklären, dass Sie ein neues Array doppelt größer als das vorhandene Array zuweisen müssen und alle diese Elemente in den neuen zugewiesenen Speicher kopieren und das alte löschen und das neue Element in das neue Array einfügen. Aber hier sind einige Schlüsselmomente, von denen ich glaube, dass sie von Ihrem Interviewer erwartet werden.

  1. Die Elemente in std :: vector werden nicht in einem Array gespeichert, daher ist das Datenelement von std :: vector [1000] kein int * der Länge 1000. Die Elemente werden in Speicherblöcken gespeichert. Daher müssen Sie beim Kopieren darauf achten.

  2. Zweitens, der Standard-stl-Vektor erfordert "gib mir ein kopierbares Objekt und ich kann es einfügen", was bedeutet, dass Requierment auf std :: vector ist, dass irgendjemand nur einen copy_constructer (nicht operator =) haben muss. Wenn Sie also einen neuen Speicher vom Typ "IrgendjArt" reservieren, müssen Sie berücksichtigen, dass sometimy keinen Standard-Kopierkonstruktor hat. In üblichen Implementierungen geschieht dies durch die Platzierung eines neuen Operators, um den Aufruf von Kopierkonstruktoren zu vermeiden.

Eduard Rostomyan 20.04.2014 20:04
quelle

Tags und Links