vector erase 주의점

Jaemyeong Lee·2024년 12월 6일

게임 서버1

목록 보기
84/220

erase가 까다로운가?

vector 내부에서 일어나는 일

  • vector는 연속 메모리이므로 중간 원소를 지우면 뒤 원소들이 앞으로 당겨집니다.
  • 이 과정에서 삭제 지점 이후 이터레이터/참조/포인터는 무효화될 수 있습니다.
  • 즉, erase 직후 "기존 it를 계속 쓰는 코드"는 매우 위험합니다.

삭제 시 메모리 이동 개념

삭제 전: [10][20][30][40][50]
             ^
             it (20)

erase(it) 후:
[10][30][40][50]
        ^
   반환된 새 it (30)

기존 it는 더 이상 신뢰 불가

잘못된 패턴 (Undefined Behavior)

for (auto it = v.begin(); it != v.end(); ++it) {
    if (*it % 2 == 0)
        v.erase(it);  // 잘못됨! it 무효화 → 크래시/스킵
}
  • erase 직후 it가 무효화될 수 있는데, 루프가 자동으로 ++it를 수행합니다.
  • 결과적으로 크래시, 요소 스킵, 간헐적 오동작이 발생할 수 있습니다.

잘못된 vs 올바른 erase 패턴

[ 잘못된 코드 ]
  v = {2, 4, 6}  삭제 조건: 짝수
  it → 2 삭제 → it 무효화!
  ++it (잘못) → 다음 위치 건너뛰거나 크래시

       삭제 전:  [2][4][6]
       it→2 삭제: [4][6]   it는?(무효)
       ++it 수행:  ??? (정의되지 않은 동작)

[ 올바른 코드 ]
  it = v.erase(it)  ← erase가 반환하는 "다음 유효한 it"로 갱신!

       삭제 전:  [2][4][6]   it→2
       erase(it): [4][6]     반환값 = 4를 가리키는 새 it
       it = 반환값 → 그 다음 ++it 또는 계속 조건 검사

올바른 패턴 (순회 중 조건 삭제)

for (auto it = v.begin(); it != v.end(); ) {
    if (*it % 2 == 0)
        it = v.erase(it);  // 반환값으로 갱신
    else
        ++it;
}
  • 삭제한 경우: it = v.erase(it); (반환된 다음 위치 사용)
  • 삭제하지 않은 경우: ++it
  • 이 패턴이 vector 조건 삭제의 기본 정답 패턴입니다.

여러 원소를 한 번에 지울 때: erase-remove_if 관용구

조건 기반으로 다수 삭제가 목적이면 아래 패턴이 간결하고 성능도 좋습니다.

v.erase(
    remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }),
    v.end()
);
  • remove_if는 "남길 원소를 앞으로 모으고" 새 논리적 끝 이터레이터를 반환합니다.
  • 마지막에 erase(new_end, v.end())로 실제 꼬리 구간을 제거합니다.
  • 반복 erase보다 보통 효율적이며(대체로 O(N)), 코드 의도가 분명합니다.
  • remove_if 사용 시 <algorithm> 헤더가 필요합니다.

end() 반환과 마지막 요소 삭제

  • 마지막 원소를 지우면 eraseend()를 반환할 수 있습니다.
  • end()는 비교용 센티널이지 역참조/증가 대상이 아닙니다.
  • 따라서 안전 패턴은 반드시 "삭제 시 대입, 비삭제 시 증가" 구조여야 합니다.

range-for에서 삭제하지 말기

아래처럼 range-for 순회 중 erase를 호출하면 내부 이터레이터와 충돌할 수 있습니다.

for (int x : v) {
    if (x % 2 == 0) {
        // v.erase(...); // 위험: range-for 내부 순회 상태와 충돌 가능
    }
}
  • range-for는 내부적으로 이터레이터를 사용합니다.
  • 삭제가 필요하면 명시적 이터레이터 루프(8-3)나 erase-remove_if(8-4)를 사용하세요.

상황별 추천 패턴

상황권장 방식
순회 중 조건부 삭제 + 세밀 제어 필요for (it ... ) + it = erase(it)
조건으로 대량 삭제erase(remove_if(...), end())
단일 위치 삭제v.erase(pos) 후 반환값 처리

체크 질문 (스스로 답해보기)

  • vector에서는 중간 erase가 이터레이터 무효화를 일으키기 쉬운가?
  • 반복 eraseerase-remove_if의 차이를 성능/가독성 관점에서 설명할 수 있는가?
  • eraseend()를 반환할 수 있는 상황을 코드로 보여줄 수 있는가?

profile
李家네_공부방

0개의 댓글