Apa yang terjadi jika Anda memanggil hapus () pada elemen peta saat iterasi dari awal hingga akhir?

133

Dalam kode berikut ini saya loop melalui peta dan menguji apakah suatu elemen perlu dihapus. Apakah aman untuk menghapus elemen dan tetap mengulanginya atau apakah saya perlu mengumpulkan kunci di wadah lain dan melakukan putaran kedua untuk memanggil penghapusan ()?

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: Tentu saja, saya kemudian membaca pertanyaan ini yang saya pikir tidak akan terkait tetapi menjawab pertanyaan saya.

Matthew Smith
sumber
Harap dicatat dalam pertanyaan yang std::remove_iftidak bekerja denganstd:map
socketpair

Jawaban:

183

C ++ 11

Ini telah diperbaiki dalam C ++ 11 (atau penghapusan telah diperbaiki / dibuat konsisten di semua jenis wadah).
Metode hapus sekarang mengembalikan iterator berikutnya.

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

Menghapus elemen di peta tidak membatalkan iterator apa pun.
(terlepas dari iterator pada elemen yang telah dihapus)

Sebenarnya memasukkan atau menghapus tidak membatalkan iterator mana pun:

Lihat juga jawaban ini:
Teknik Tebusan Markus

Tetapi Anda perlu memperbarui kode Anda:
Dalam kode Anda, Anda menambah pm_it setelah memanggil hapus. Pada titik ini sudah terlambat dan sudah tidak valid.

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
    }
}
Martin York
sumber
Apakah urutan evaluasi kenaikan pada ekspresi postfix pm_it++dijamin akan dieksekusi sebelum fungsi dimasukkan?
David Rodríguez - dribeas
4
@ David Rodríguez - dribea: Ya. Standar menjamin bahwa semua ekspresi argumen akan sepenuhnya dievaluasi sebelum fungsi dipanggil. Ini adalah hasil kenaikan pos yang dilewatkan ke fungsi hapus (). Jadi ya penambahan postingan pm_it akan dilakukan sebelum erase () dipanggil.
Martin York
CATATAN: Hampir baris untuk baris cocok dengan contoh wadah asosiatif di Scott Meyer "Effective STL" Item 9.
Ogre Psalm33
for (auto pm_t = port_map.begin (); pm_it! = port_map.end ();) {...}
Andrey Syrokomskiy
4
@iboisver: Di vektor. Penggunaan erase () membatalkan semua iterator dari array setelah titik erase (bukan hanya akhir), ini adalah properti Sequencekontainer. Properti khusus dari Associativewadah adalah iterator tidak disahkan dengan menghapus atau menyisipkan (kecuali jika menunjuk pada elemen yang telah dihapus). Vektor dan hapus itignators usign dibahas secara rinci dalam pertanyaan yang sesuai stackoverflow.com/a/3938847/14065
Martin York
12

Begini cara saya melakukan itu ...

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 ...
     }
}
AlaaShaker
sumber
Jika Anda juga menghapus ujung vektor (itEnd), maka pemeriksaan terakhir (kondisi while) akan melawan iterator yang tidak valid (itEnd). Tidak baik.
Agostino
1

Inilah yang akan saya lakukan, kira-kira:

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

Ada sesuatu yang aneh

val.second == delete_this_id

tapi saya baru saja menyalinnya dari kode contoh Anda.

ravenspoint
sumber