처음부터 끝까지 반복하는 동안지도 요소에서 erase ()를 호출하면 어떻게됩니까?


133

다음 코드에서는 맵을 반복하고 요소를 지워야하는지 테스트합니다. 요소를 지우고 계속 반복하는 것이 안전합니까 아니면 다른 컨테이너에서 키를 수집하고 두 번째 루프를 수행하여 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);
    }
}

업데이트 : 물론, 나는 관련이 없다고 생각했지만 내 질문에 대답하는 이 질문읽었습니다 .


std::remove_if작동하지 않는 문제에 유의하십시오.std:map
socketpair

답변:


183

C ++ 11

이것은 C ++ 11에서 수정되었습니다 (또는 모든 컨테이너 유형에서 지우기가 향상되거나 일관되게되었습니다).
erase 메소드는 이제 다음 반복자를 리턴합니다.

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

맵에서 요소를 지워도 반복자가 무효화되지 않습니다.
(삭제 된 요소의 반복자 제외)

실제로 삽입하거나 삭제해도 반복자가 무효화되지 않습니다.

이 답변 참조 :
Mark Ransom Technique

그러나 코드를 업데이트해야합니다.
코드에서 지우기를 호출 한 후 pm_it를 증가시킵니다. 이 시점에서 너무 늦었고 이미 무효화되었습니다.

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

접미사 표현식의 증분 평가 순서 pm_it++는 함수가 입력되기 전에 실행되도록 보장됩니까?
David Rodríguez-dribeas

4
@David Rodríguez-dribeas : 예. 표준은 함수가 호출되기 전에 모든 인수 표현식이 완전히 평가되도록 보장합니다. 소거 함수 ()에 전달 된 포스트 증분의 결과입니다. 따라서 pm_it의 포스트 증가는 erase ()가 호출되기 전에 수행됩니다.
Martin York

참고 : 행의 거의 줄은 Scott Meyer의 "Effective STL"항목 9의 연관 컨테이너 예제와 일치합니다.
Ogre Psalm33

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

4
@iboisver : 벡터에. erase ()를 사용하면 소거 지점 (끝이 아닌) 이후 배열의 모든 반복자가 무효화됩니다. 이것은 Sequence컨테이너 의 속성입니다 . Associative컨테이너 의 특별한 속성은 반복자가 지우기 또는 삽입에 의해 무효화되지 않는다는 것입니다 (삭제 된 요소를 가리 키지 않는 한). 벡터 및 지우기 usign 반복자가 적절한 질문에 자세히 설명되어 있습니다. stackoverflow.com/a/3938847/14065
Martin York

12

내가하는 방법은 다음과 같습니다 ...

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

벡터의 끝 (itEnd)도 삭제하면 마지막 검사 (while 조건)는 무효화 된 반복자 (itEnd)에 대한 것입니다. 안좋다.
Agostino

1

이것이 내가 대략하는 방법입니다.

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

이상한 점이 있습니다

val.second == delete_this_id

하지만 방금 예제 코드에서 복사했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.