it-swarm.com.de

Was passiert, wenn Sie erase () für ein Kartenelement aufrufen, während Sie von Anfang bis Ende iterieren?

Im folgenden Code durchlaufe ich eine Karte und teste, ob ein Element gelöscht werden muss. Ist es sicher, das Element zu löschen und weiter zu iterieren, oder muss ich die Schlüssel in einem anderen Container sammeln und eine zweite Schleife ausführen, um erase () aufzurufen?

map<string, SerialdMsg::SerialFunction_t>::iterator pm_it;
for (pm_it = port_map.begin(); pm_it != port_map.end(); pm_it++)
{
    if (pm_it->second == delete_this_id) {
        port_map.erase(pm_it->first);
    }
}

UPDATE: Natürlich habe ich dann lies diese Frage was ich nicht für verwandt gehalten hätte, aber meine Frage beantwortet.

132
Matthew Smith

C++ 11

Dies wurde in C++ 11 behoben (oder das Löschen wurde für alle Containertypen verbessert/konsistent gemacht).
Die Löschmethode gibt jetzt den nächsten Iterator zurück.

auto pm_it = port_map.begin();
while(pm_it != port_map.end())
{
    if (pm_it->second == delete_this_id)
    {
        pm_it = port_map.erase(pm_it);
    }
    else
    {
        ++pm_it;
    }
}

C++ 03

Das Löschen von Elementen in einer Map macht keine Iteratoren ungültig.
(abgesehen von Iteratoren für das Element, das gelöscht wurde)

Das tatsächliche Einfügen oder Löschen macht keinen der Iteratoren ungültig:

Siehe auch diese Antwort:
Mark Ransom Technique

Sie müssen jedoch Ihren Code aktualisieren:
In Ihrem Code erhöhen Sie pm_it, nachdem Sie erase aufgerufen haben. Zu diesem Zeitpunkt ist es zu spät und ist bereits ungültig.

map<string, SerialdMsg::SerialFunction_t>::iterator pm_it = port_map.begin();
while(pm_it != port_map.end())
{
    if (pm_it->second == delete_this_id)
    {
        port_map.erase(pm_it++);  // Use iterator.
                                  // Note the post increment.
                                  // Increments the iterator but returns the
                                  // original value for use by erase 
    }
    else
    {
        ++pm_it;           // Can use pre-increment in this case
                           // To make sure you have the efficient version
    }
}
183
Martin York

So mache ich das ...

typedef map<string, string>   StringsMap;
typedef StringsMap::iterator  StrinsMapIterator;

StringsMap m_TheMap; // Your map, fill it up with data    

bool IsTheOneToDelete(string str)
{
     return true; // Add your deletion criteria logic here
}

void SelectiveDelete()
{
     StringsMapIter itBegin = m_TheMap.begin();
     StringsMapIter itEnd   = m_TheMap.end();
     StringsMapIter itTemp;

     while (itBegin != itEnd)
     {
          if (IsTheOneToDelete(itBegin->second)) // Criteria checking here
          {
               itTemp = itBegin;          // Keep a reference to the iter
               ++itBegin;                 // Advance in the map
               m_TheMap.erase(itTemp);    // Erase it !!!
          }
          else
               ++itBegin;                 // Just move on ...
     }
}
12
AlaaShaker

So würde ich es ungefähr machen:

bool is_remove( pair<string, SerialdMsg::SerialFunction_t> val )
{
    return val.second == delete_this_id;
}

map<string, SerialdMsg::SerialFunction_t>::iterator new_end = 
    remove_if (port_map.begin( ), port_map.end( ), is_remove );

port_map.erase (new_end, port_map.end( ) );

Es ist etwas Seltsames

val.second == delete_this_id

aber ich habe es gerade aus deinem Beispielcode kopiert.

1
ravenspoint