[STL]Wert aus einer Multimap loeschen
-
Hallo,
ich habe eine Klasse geschrieben, welche mittles einer Multimap strings mit Integers verknuepft(multimap<string,int>). Die Klasse hat 3 Methoden: check(string), insert(string,int) und delete(string, int).
Mittels check(string) werden der Reihe nach alle Integer abgearbeitet (In einer weiteren map ist jedem Integer ein Zeiger auf ein Objekt zugeordnet, welches aufgerufen wird, bzw. eine Methode des Objekts), welche mit dem string assoziiert sind:map<int,meineklasse*> objektmap; multimap<string,int> MeineMultimap; multimap<string,int>::iterator iter; multimap<string,int>::iterator iter_low; multimap<string,int>::iterator iter_up; iter_low=MeineMultimap.lower_bound(MeinString); iter_up =MeineMultimap.upper_bound(MeinString); for(iter=iter_low; iter < iter_up; iter++) { objektmap[iter->second]->meinemethode(); }Mittels insert(string,int) wird ein neuer Wert erstellt:
insert(string Tanga, int Integaer) { objektmap.insert(make_pair(Integaer, new meineklasse())); MeineMultimap.insert(make_pair(Tanga, Integaer)); }Und nun kommt glaube ich (glaube ich nur
deshalb hab ich mal so ziemlich alles was mein Problem betreffen koennte hier gepostet) der Knackpunkt und mein Problem.
Mittels delete wird ein Wert/Integer aus der Multimap geloescht, damit er nicht mehr bei aufrufen von check ¨ausgefuehrt¨ wird:delete(string Tanga, int Integaer) { delete objektmap[Integaer]; objektmap.erase(Integaer); multimap<string,int>::iterator iter; multimap<string,int>::iterator upper; multimap<string,int>::iterator lower; vector<int> puffer; upper=MeineMultimap.upper_bound(Tanga); lower=MeineMultimap.lower_bound(Tanga); for(iter=lower; iter!=upper; iter++) { puffer.push_back(iter->second); } MeineMultimap.erase(Tanga); for(int i=0; i<puffer.size(); i++) { if(puffer[i]!=Integaer) MeineMultimap.insert(make_pair(Tanga, puffer[i])); } }Ich gehe dabei so vor, dass ich erst die kompletten Werte, welche zu dem jeweiligen Key vorhanden sind kopiere, den Key dann loesche und den Key neu erstelle mit allen gespeicherten werten in puffer, nur ohne den zu loeschenden Wert.
Ich finde das ganze sieht schon so aus, als koennte es funktionieren
das Problem ist jedoch, dass nun nach einem Aufruf von delete und einem darauf folgendem Aufruf von check, zwar nichtmehr das Objekt mit dem Integer, welcher durch delete geloescht werden sollte aufgerufen wird. Jedoch wird nun (glaube ich) der Integer des naechsten strings verarbeitet. Irgendwie funktioniert das loeschen aus der Multimap nicht. Oder kann es sein, dass wenn ich check aufrufe, ohne, dass zu dem Key ein Wert in der Multimap hinterlegt ist (bzw. der Key gar nicht existiert) ¨Mist¨ herauskommt (genauer ein falscher int-wert aufgerufen wird)?Ich hoffe ich hab das ganze einigermassen verstaendlich erklaert und dass mir jemand helfen kann... bin schon langsam am Verzweifeln

Auf jeden Fall schon mal vielen Dank!

Achja: man moege mir verzeihen, dass ich von integers geredet habe, welche aufgerufen werden, damit meinte ich naetuerlich das damit assoziierte Objekt(bzw. der damit verbundene Zeiger
). Ich denke aber, dass es so verstaendlicher ist.
-
Erstens kannst du erase() auch einen Iterator übergeben, dann löscht er nur das entsprechende Objekt (und du mußt den Rest nicht neu einfügen).
Zweitens könntest du mal mit dem Debugger verfolgen, was wirklich passiert, anstatt dich auf Vermutungen zu verlassen.
(und drittens solltest du sicherstellen, daß du nicht zwei Objekte mit der selben ID anlegst - oder die Struktur verflachen, indem du direkt eine multimap<string,meineklasse*> verwendest)
-
Hallo,
Tipp 1: statt erst lower_bound und dann upper_bound zu benutzen, wäre es einfacher, wenn du gleich equal_range anwenden würdest.Tipp 2: map::operator[key] erzeugt ein default-konstruiertes Objekt für key, falls key noch nicht in der Map existiert. Falls du dir also nicht sicher bist, dass key existiert, solltest du lieber find verwenden.
typedef std::multimap<std::string,int> TheMultiMap; typedef std::map<int,meineklasse*> TheObjectMap; typedef TheMultiMap::iterator iterator; typedef std::pair<iterator, iterator> Range; void check(const std::string& aString) { for (Range r = theMap.equal_range(aString); r.first != r.second; ++r.first) { TheObjectMap::iterator it = objectMap.find(r.first->second); if (it != objectMap.end()) { it->second->aMethod(); } } }Tipp 3: objektmap.insert(make_pair(Integaer, new meineklasse())); erzeugt ein Speicherloch, falls in der Map bereits ein Eintrag mit dem Key Integaer (korrekt: Integer) existiert. Besser:
TheObjectMap::iterator it = objectMap.lower_bound(anInt); if (it == objectMap.end() || it->first != anInt) { objectMap.insert(it, make_pair(anInt, new MeineKlasse())); theMap.insert(make_pair(aString, anInt)); }Tipp 4: das Löschen geht effizienter:
void remove(const std::string& aString, int anInt) { TheObjectMap::iterator it = objectMap.find(anInt); if (it != objectMap.end()) { delete it->second; objectMap.erase(it); } Range r = theMap.equal_range(aString); for (iterator it = r.first; it != r.second;) { if (it->second == anInt) { theMap.erase(it++); } else { ++it; } } }Was mich an deinem Code irritiert ist die Abbildung, die du gewählt hast, da diese nicht eindeutig ist. Angenommen du hast folgenden Ablauf:
insert("Hallo", 1);
insert("Welt", 1);
remove("Hallo", 1);
check();In diesem Fall enthält deine Multi-Map noch den Wert <"Welt", 1>, deine Objektmap besitzt aber kein Objekt für 1 mehr.
-
Was mich an deinem Code irritiert ist die Abbildung, die du gewählt hast, da diese nicht eindeutig ist. Angenommen du hast folgenden Ablauf:
insert("Hallo", 1);
insert("Welt", 1);
remove("Hallo", 1);
check();In diesem Fall enthält deine Multi-Map noch den Wert <"Welt", 1>, deine Objektmap besitzt aber kein Objekt für 1 mehr.
du hast Recht, das ganze ist ziemlich unschön, aber der restliche Aufbau des Programms stellt sicher, dass jeder Integer-Wert einmalig ist. Das gleiche trifft auf das Problem des Speicherlochs beim Aufruf von 2 inserts mit dem gleichen integer-Wert zu.
Ich weiss, dass das keine wirklich schöne Lösung ist, mir war es nur irgendwie nie wirklich bewusst
werde das auf jeden Fall ändern. Danke!Erstens kannst du erase() auch einen Iterator übergeben, dann löscht er nur das entsprechende Objekt (und du mußt den Rest nicht neu einfügen).
So hatte ich das ganze zu Erst auch geschrieben. Nur gibt es da das Problem, dass sobald erase mit dem Iterator aufgerufen wurde, der Iterator ungültig ist und somit die Schleife, welche von der lower zur upper-bound loopt nichtmehr korrekt ausgeführt wird (kann zu einer Endlosschleife kommen)
Und das mit dem Debugger ist so eine Sache... normalerweise programmiere ich unter MaxOSX mit XCode, wo es einen wunderbaren Debugger gibt (bzw. einen fuer jeden Idioten benutzbaren
). Eine Api, welche ich in dem Projekt benutze ist nun jedoch nicht fuer MacOSX verfügbar (endian-Problem und bestimmte system-calls bei OSX nicht vorhanden), so muss ich nun auf Linux ausweichen und da steige ich noch nicht wirklich durch gdb bzw. ddd durch.Euch beiden auf jeden Fall vielen Dank für die Tips, werde das ganze dann mal nach euren Vorschlägen umschreiben und mich bei Erfolg/Misserfolg
wieder melden. Vielen Dank!
-
So, ich habs nun ausprobiert und es funktioniert alles! Vielen Dank!
Ein Problem war auch, dass ein Objekt, welches durch check(string) aufgerufen wird für sich selbst ein delete(int) aufgerufen hat. Das kann natürlich nicht funktionieren...Vielen Dank auf jeden Fall an euch beide!