Baştan sona yineleme sırasında bir harita öğesinde erase () öğesini çağırırsanız ne olur?


133

Aşağıdaki kodda bir harita boyunca döngü yapıyorum ve bir elemanın silinmesi gerekip gerekmediğini test ediyorum. Elemanı silmek ve yinelemeyi sürdürmek güvenli mi yoksa anahtarları başka bir kapta toplamam ve silmeyi () çağırmak için ikinci bir döngü yapmam gerekir mi?

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);
    }
}

GÜNCELLEME: Tabii ki, sonra ilgili olacağını düşünmediğim ancak sorumu cevaplayan bu soruyu okudum .


Lütfen std::remove_ifçalışmayan soruya dikkat edinstd:map
socketpair

Yanıtlar:


183

C ++ 11

Bu C ++ 11 ile giderildi (veya silme işlemi tüm kap türlerinde iyileştirildi / tutarlı hale getirildi).
Silme yöntemi şimdi bir sonraki yineleyiciyi döndürür.

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

Bir haritadaki öğelerin silinmesi, yineleyicileri geçersiz kılmaz.
(silinmiş öğedeki yineleyiciler dışında)

Aslında ekleme veya silme yineleyicilerin hiçbirini geçersiz kılmaz:

Ayrıca bu cevaba bakınız:
Mark Ransom Tekniği

Ancak kodunuzu güncellemeniz gerekir:
Kodunuzda, silme işlemini çağırdıktan sonra pm_it değerini artırırsınız. Bu noktada çok geç ve zaten geçersiz.

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
    }
}

Postfix ifadesindeki artışın değerlendirme sırasının pm_it++, işleve girilmeden önce yürütülmesi garanti ediliyor mu?
David Rodríguez - dribeas

4
@David Rodríguez - dribeas: Evet. Standart, işlev çağrılmadan önce tüm argüman ifadelerinin tam olarak değerlendirileceğini garanti eder. Silme işlevine () iletilen artım artışının sonucudur. Yani evet, pm_it öğesinin artım artışı silme () çağrılmadan önce yapılacaktır.
Martin York

NOT: Hat için neredeyse çizgi, Scott Meyer'ın "Etkili STL "'
sindeki

için (otomatik pm_t = port_map.begin (); pm_it! = port_map.end ();) {...}
Andrey Syrokomskiy

4
@iboisver: Vektör üzerinde. Erase () kullanımı, silme noktasından sonra dizinin tüm yineleyicilerini geçersiz kılar (yalnızca son değil), bu Sequencekapların bir özelliğidir . AssociativeKapların özel özelliği, yineleyicilerin silme veya ekleme ile geçersiz kılınmamasıdır (silinmiş öğeye işaret etmedikçe). Vektör ve silme usign yineleyicileri ilgili soruda ayrıntılı olarak ele alınmıştır stackoverflow.com/a/3938847/14065
Martin York

12

İşte böyle yapıyorum ...

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 ...
     }
}

Vektörün sonunu da silerseniz (itEnd), son kontrol (while koşulu) geçersiz kılınmış bir yineleyiciye (itEnd) karşı olacaktır. İyi değil.
Agostino

1

Ben böyle yapardım, yaklaşık:

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( ) );

Garip bir şey var

val.second == delete_this_id

ama ben sadece örnek kodunuzdan kopyaladım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.