Điều gì xảy ra nếu bạn gọi erase () trên một phần tử bản đồ trong khi lặp từ đầu đến cuối?


133

Trong đoạn mã sau tôi lặp qua bản đồ và kiểm tra xem có cần xóa phần tử không. Có an toàn để xóa phần tử và tiếp tục lặp lại hay tôi cần thu thập các khóa trong một thùng chứa khác và thực hiện một vòng lặp thứ hai để gọi erase ()?

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

CẬP NHẬT: Tất nhiên, sau đó tôi đọc câu hỏi này mà tôi không nghĩ sẽ liên quan nhưng trả lời câu hỏi của tôi.


Xin lưu ý trong câu hỏi std::remove_ifkhông hoạt động vớistd:map
thất vọng

Câu trả lời:


183

C ++ 11

Điều này đã được sửa trong C ++ 11 (hoặc xóa đã được cải thiện / thực hiện nhất quán trên tất cả các loại container).
Phương thức xóa bây giờ trả về iterator tiếp theo.

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

Xóa các phần tử trong bản đồ không làm mất hiệu lực bất kỳ trình lặp nào.
(ngoài các trình lặp trên phần tử đã bị xóa)

Trên thực tế, việc chèn hoặc xóa không làm mất hiệu lực của bất kỳ trình lặp nào:

Cũng xem câu trả lời này:
Kỹ thuật Mark Ransom

Nhưng bạn cần phải cập nhật mã của mình:
Trong mã của bạn, bạn tăng pm_it sau khi gọi xóa. Tại thời điểm này, nó đã quá muộn và đã bị vô hiệu.

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

Là thứ tự đánh giá mức tăng trong biểu thức postfix có pm_it++được đảm bảo thực hiện trước khi hàm được nhập không?
David Rodríguez - dribeas

4
@David Rodríguez - dribeas: Vâng. Tiêu chuẩn đảm bảo rằng tất cả các biểu thức đối số sẽ được đánh giá đầy đủ trước khi hàm được gọi. Nó là kết quả của việc tăng bài được truyền cho hàm erase (). Vì vậy, có, việc tăng bài của pm_it sẽ được thực hiện trước khi erase () được gọi.
Martin York

LƯU Ý: Hầu như dòng cho dòng khớp với ví dụ về thùng chứa kết hợp trong "STL hiệu quả" của Scott Meyer Mục 9.
Ogre Psalm33

for (auto pm_t = port_map.begin (); pm_it! = port_map.end ();) {...}
Andrey Syrokomskiy

4
@iboisver: Trên vector. Việc sử dụng erase () làm mất hiệu lực tất cả các lần lặp của mảng sau điểm xóa (không chỉ kết thúc), đây là một thuộc tính của các Sequencecontainer. Thuộc tính đặc biệt của các Associativecontainer là các trình vòng lặp không bị vô hiệu bằng cách xóa hoặc chèn (trừ khi chúng trỏ vào phần tử đã bị xóa). Các vòng lặp vectơ và xóa usign được trình bày chi tiết trong câu hỏi thích hợp stackoverflow.com/a/3938847/14065
Martin York

12

Đây là cách tôi làm điều đó ...

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

Nếu bạn cũng xóa phần cuối của vectơ (itEnd), thì lần kiểm tra cuối cùng (điều kiện while) sẽ chống lại một trình vòng lặp không hợp lệ (itEnd). Không tốt.
Agostino

1

Đây là cách tôi sẽ làm điều đó, khoảng:

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

Có điều gì đó kỳ lạ về

val.second == delete_this_id

nhưng tôi chỉ sao chép nó từ mã ví dụ của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.