2010-07-20 11 views
29

Ich habe eine std::vector mit Elementen einer Klasse ClassA. Zusätzlich möchte ich einen Index unter Verwendung einer std::map<key,ClassA*> erstellen, die einen Schlüsselwert auf Zeiger auf Elemente abbildet, die in dem Vektor enthalten sind.Zeiger auf Elemente von Std :: Vektor und Std :: Liste

gibt es keine Garantie, dass diese Zeiger gültig bleiben (und auf dasselbe Objekt) wenn Elemente hinzugefügt sind am Ende des Vektors (nicht eingefügt). Das heißt, würde der folgende Code korrekt sein:

std::vector<ClassA> storage; 
std::map<int, ClassA*> map; 

for (int i=0; i<10000; ++i) { 
    storage.push_back(ClassA()); 
    map.insert(std::make_pair(storage.back().getKey(), &(storage.back())); 
} 
// map contains only valid pointers to the 'correct' elements of storage 

Wie ist die Situation, wenn ich std::list statt std::vector?

+1

Was ist der Zweck des Vektors hier? Müssen Sie sich an die Reihenfolge erinnern, in der sie erstellt wurden? Sie könnten stattdessen die Karte und vecor verwenden. Iteratoren/Zeiger/Verweise auf Elemente einer Karte bleiben länger gültig. Sehen Sie sich die Garantien Ihrer bevorzugten Standard-Bibliotheksreferenz an. – sellibitze

Antwort

24

Vektoren - Nein. Da die Kapazität von Vektoren niemals abnimmt, ist sichergestellt, dass Verweise, Zeiger und Iteratoren auch beim Löschen oder Ändern von Elementen gültig bleiben, sofern sie sich auf eine Position vor den manipulierten Elementen beziehen. Einfügungen können jedoch Verweise, Zeiger und Iteratoren ungültig machen.

Listen - Ja, Einfügen und Löschen von Elementen nicht ungültig Zeiger, Referenzen und Iteratoren zu anderen Elementen

+1

Antworten sollten umgekehrt werden, Vektoren -> nein und Listen -> ja, wie die Frage ist "Gibt es eine Garantie, dass diese Zeiger gültig bleiben?" – Naveen

+0

@Naveen - Edited wie hervorgehoben, danke. – DumbCoder

+0

Ein "Deque" kann auch eine gute Wahl sein, wenn ein zufälliger Zugriff und keine Neuzuweisung beim Hinzufügen von Elementen gewünscht wird. – foraidt

9

Soweit ich weiß, gibt es keine solche Garantie. Das Hinzufügen von Elementen zum Vektor führt zu einer Neuzuordnung von Elementen, wodurch alle Zeiger in der Karte ungültig werden.

+0

Das habe ich mir gedacht. Kennst du 'std :: list'? Schließlich, wenn es als verkettete Liste implementiert wird, würde es keine Notwendigkeit für Neuzuweisung geben ... – MartinStettner

+1

Ich denke * kann verursachen * ist passenderer Wortlaut. Und trotzdem kann ich erwarten, dass "Realloc" in der internen Implementierung verwendet wird, was wiederum die Zeiger brechen kann. –

1

machen sie einfach beide speichern Zeiger eine explizit die Objekte löschen, wenn Sie sie nicht brauchen.

std::vector<ClassA*> storage; 
std::map<int, ClassA*> map; 

for (int i=0; i<10000; ++i) { 
    ClassA* a = new ClassA() 
  storage.push_back(a) 
  map.insert(std::make_pair(a->getKey(), a)) 
} 
// map contains only valid pointers to the 'correct' elements of storage 
+3

Ich würde dringend davon abraten, nackte Zeiger in einem STL-Container zu speichern. Es ist ein Rezept für Lecks. – sbi

+0

Hm, genau das versuche ich zu vermeiden :). Ich könnte in diesem Fall nur die Map verwenden (für mein Problem), ich möchte nur einen Container haben, der sich um die Speicherfreigabe kümmert. – MartinStettner

+0

Danke für die Bearbeitung (auf dem iPad und kann keine Semikolons formatieren oder setzen). – Tom

3

Ich bin mir nicht sicher, ob es garantiert wird, aber in der Praxis sollte storage.reserve(needed_size) sicherstellen, dass keine Umschichtungen auftreten.

Aber warum speichern Sie keine Indizes?
Es ist einfach, Indizes in Iteratoren zu konvertieren, indem Sie sie dem begin iterator hinzufügen (storage.begin()+idx) und es ist einfach, jeden Iterator in einen Zeiger zu verwandeln, indem Sie ihn zuerst dereferenzieren und dann seine Adresse nehmen (&*(storage.begin()+idx)).

+0

Das Problem ist, dass ich 'needs_size' nicht im Voraus kenne (ich gebe zu, dass der Code etwas vereinfacht ist ...) Das Speichern von Indizes wäre eine Option, aber ich muss auch Zeiger auf verschiedene andere Teile des Programms übergeben die keinen Zugriff auf den Vektor haben sollte (der Code zeigt diesen Aspekt nicht wieder) – MartinStettner

+0

@MartinStettner: Sie können Indizes leicht in Zeiger für einen Vektor umwandeln. Ich habe meine Antwort erweitert, um das zu erklären. – sbi

+1

Das Ganze ist in eine Klasse eingekapselt, die Zeiger an die "Außenseite" übergeben muss, andere Teile des Programms können diese Zeiger auch speichern, also müssen sie konstant sein. Wenn ich Ihren Ansatz verwenden würde, müsste ich auch den begin() Iterator bereitstellen, was eine Verletzung der Kapselung wäre (der Vektorspeicher sollte ein internes Implementierungsdetail sein ...). – MartinStettner

6

Verwenden Sie std::deque! Zeiger auf die Elemente sind stabil, wenn nur push_back() verwendet wird.

Hinweis: Iteratoren zu Elementen können ungültig sein! Zeiger auf Elemente werden nicht angezeigt.

Edit: diese Antwort erklärt die Details, warum: C++ deque's iterator invalidated after push_front()

+1

Sind Sie sich sicher? Gibt es einen Teil des C++ - Standards, der diesen Anspruch abdeckt? Es könnte die meiste Zeit auf diese Weise implementiert werden, aber ich brauche eine Art Garantie ... – MartinStettner

+0

Warum sollte ein Iterator, der im Grunde ein Zeiger * speziell für diesen Container * entworfen ist, für ungültig erklärt werden, aber nicht ein roher Zeiger, der stellt nichts als eine Speicheradresse (und einen Typ) dar? – foraidt

+2

@mxp: Ein Iterator muss das nächste Element finden können. Diese Fähigkeit erfordert zusätzliche Informationen im Iterator, und diese zusätzlichen Informationen werden möglicherweise ungültig gemacht. – Sjoerd

1

Von einer der Kommentare auf eine andere Antwort, es scheint, als ob alles, was Sie wollen zentralisieren (problemloser) Speicherverwaltung. Wenn das wirklich der Fall ist, sollten Sie vordefinierte Lösungen wie die boost pointer container-Bibliothek in Erwägung ziehen und Ihren eigenen Code so einfach wie möglich halten.

Insbesondere werfen Sie einen Blick auf ptr_map

+0

Vielen Dank, dass Sie darauf hingewiesen haben. Leider ist dieses Projekt für einen großen Kunden gedacht, der die Boost-Bibliothek (noch) nicht in seinen Code aufnehmen möchte (obwohl das viele Probleme lindern würde :) ...) – MartinStettner

0
  1. für Vektoren keine.
  2. für Listen ja. wie? Iterator arbeitet als Zeiger auf einen bestimmten Knoten in der Liste. so können Sie Werte zu jeder Struktur zuweisen wie:

    Liste Myliste;

    Paar < Liste :: Iterator, Int> Temp;

    temp = make_pair (mylist.begin(), x);

+0

Das ist wörtlich die selbe Antwort wie [DumbCoders] (https://Stackoverflow.com/a/3287828/501011), nur 7 Jahre zu spät und in jeder Hinsicht schlechter –

+0

Ich hatte das ähnliche Problem und war auf der Suche nach einem einfachen Beispiel. Da es in keiner der obigen Antworten vorhanden war, dachte ich mir selbst ein Beispiel zu schreiben. –