C++ 반복자 | "Iterator" 완벽 가이드

C++ 반복자 | "Iterator" 완벽 가이드

이 글의 핵심

반복자(iterator) 는 컨테이너 요소를 순회하는 객체입니다. 범위 기반 for와 vector에서 begin/end로 쓰이며, size_t·ptrdiff_t로 거리·인덱스를 다룰 때와 Composite 패턴으로 트리 순회를 통일할 때도 자주 씁니다.

반복자 기본

반복자(iterator) 는 컨테이너 요소를 순회하는 객체입니다. 범위 기반 forvector에서 begin/end로 쓰이며, size_t·ptrdiff_t로 거리·인덱스를 다룰 때와 Composite 패턴으로 트리 순회를 통일할 때도 자주 씁니다.

반복자 개념도

graph LR
    A[Container] --> B[begin]
    A --> C[end]

    B --> D[Iter]
    D -->|++| E[Iter]
    E -->|++| F[Iter]
    F -->|++| C
    
    D -->|*| G[Elem 1]
    E -->|*| H[Elem 2]
    F -->|*| I[Elem 3]
#include <vector>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5};
    
    // begin/end
    for (auto it = v.begin(); it != v.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    
    // 역방향
    for (auto it = v.rbegin(); it != v.rend(); ++it) {
        cout << *it << " ";  // 5 4 3 2 1
    }
}

반복자 종류

반복자 계층 구조

graph TD
    A[Input Iterator] --> C[Forward Iterator]
    B[Output Iterator] --> C
    C --> D[Bidirectional Iterator]
    D --> E[Random Access Iterator]
    E --> F[Contiguous Iterator C++20]
    
    A -.->|읽기 전용| A1[istream_iterator]
    B -.->|쓰기 전용| B1[ostream_iterator]
    C -.->|단방향| C1[forward_list]
    D -.->|양방향| D1[list, set, map]
    E -.->|임의 접근| E1[vector, deque, array]
    F -.->|연속 메모리| F1[vector, array, string]

반복자 기능 비교표

반복자 타입읽기쓰기전진후진임의 접근예시 컨테이너
Inputistream_iterator
Outputostream_iterator
Forwardforward_list
Bidirectionallist, set, map
Random Accessvector, deque

Input Iterator

// 읽기 전용, 한 번만 순회
istream_iterator<int> in(cin);
istream_iterator<int> eof;

vector<int> v(in, eof);  // 입력 읽기

Output Iterator

// 쓰기 전용
ostream_iterator<int> out(cout, " ");

vector<int> v = {1, 2, 3};
copy(v.begin(), v.end(), out);  // 1 2 3

Forward Iterator

// 읽기/쓰기, 여러 번 순회
forward_list<int> fl = {1, 2, 3};

for (auto it = fl.begin(); it != fl.end(); ++it) {
    *it *= 2;
}

Bidirectional Iterator

// 양방향 이동
list<int> l = {1, 2, 3, 4, 5};

auto it = l.end();
--it;  // 마지막 원소
cout << *it << endl;  // 5

Random Access Iterator

// 임의 접근
vector<int> v = {1, 2, 3, 4, 5};

auto it = v.begin();
it += 3;  // 3칸 이동
cout << *it << endl;  // 4

cout << v[2] << endl;  // 3

실전 예시

예시 1: 커스텀 반복자

class Range {
private:
    int current;
    int end;
    
public:
    class Iterator {
    private:
        int value;
        
    public:
        using iterator_category = forward_iterator_tag;
        using value_type = int;
        using difference_type = ptrdiff_t;
        using pointer = int*;
        using reference = int&;
        
        Iterator(int v) : value(v) {}
        
        int operator*() const { return value; }
        
        Iterator& operator++() {
            ++value;
            return *this;
        }
        
        bool operator!=(const Iterator& other) const {
            return value != other.value;
        }
    };
    
    Range(int start, int end) : current(start), end(end) {}
    
    Iterator begin() { return Iterator(current); }
    Iterator end() { return Iterator(this->end); }
};

int main() {
    for (int x : Range(0, 10)) {
        cout << x << " ";  // 0 1 2 ... 9
    }
}

예시 2: 필터 반복자

template<typename Iter, typename Pred>
class FilterIterator {
private:
    Iter current;
    Iter end;
    Pred predicate;
    
    void advance() {
        while (current != end && !predicate(*current)) {
            ++current;
        }
    }
    
public:
    FilterIterator(Iter begin, Iter end, Pred pred)
        : current(begin), end(end), predicate(pred) {
        advance();
    }
    
    auto operator*() const { return *current; }
    
    FilterIterator& operator++() {
        ++current;
        advance();
        return *this;
    }
    
    bool operator!=(const FilterIterator& other) const {
        return current != other.current;
    }
};

int main() {
    vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    auto isEven =  { return x % 2 == 0; };
    
    FilterIterator begin(v.begin(), v.end(), isEven);
    FilterIterator end(v.end(), v.end(), isEven);
    
    for (auto it = begin; it != end; ++it) {
        cout << *it << " ";  // 2 4 6 8 10
    }
}

예시 3: 변환 반복자

template<typename Iter, typename Func>
class TransformIterator {
private:
    Iter current;
    Func transform;
    
public:
    TransformIterator(Iter it, Func f) : current(it), transform(f) {}
    
    auto operator*() const {
        return transform(*current);
    }
    
    TransformIterator& operator++() {
        ++current;
        return *this;
    }
    
    bool operator!=(const TransformIterator& other) const {
        return current != other.current;
    }
};

int main() {
    vector<int> v = {1, 2, 3, 4, 5};
    
    auto square =  { return x * x; };
    
    TransformIterator begin(v.begin(), square);
    TransformIterator end(v.end(), square);
    
    for (auto it = begin; it != end; ++it) {
        cout << *it << " ";  // 1 4 9 16 25
    }
}

반복자 연산

반복자 연산 함수 비교

함수시간 복잡도지원 반복자설명
advance(it, n)O(1) ~ O(N)모든 반복자n칸 이동 (제자리 수정)
distance(first, last)O(1) ~ O(N)Input 이상두 반복자 간 거리
next(it, n=1)O(1) ~ O(N)Forward 이상n칸 앞 반복자 반환
prev(it, n=1)O(1) ~ O(N)Bidirectional 이상n칸 뒤 반복자 반환
iter_swap(it1, it2)O(1)Forward 이상두 반복자 값 교환
vector<int> v = {1, 2, 3, 4, 5};

auto it = v.begin();

// 전진
advance(it, 3);  // 3칸 이동
cout << *it << endl;  // 4

// 거리
auto dist = distance(v.begin(), v.end());
cout << dist << endl;  // 5

// 다음/이전
auto next_it = next(it);
auto prev_it = prev(it);

advance vs next 차이:

graph LR
    A[it = v.begin] --> B{Which?}
    
    B -->|advance it, 3| C[Modify it]
    C --> D[it = begin+3]
    
    B -->|next it, 3| E[Return new]
    E --> F[it = begin]
    E --> G[return = begin+3]

자주 발생하는 문제

문제 1: 무효화된 반복자

// ❌ 반복자 무효화
vector<int> v = {1, 2, 3, 4, 5};

for (auto it = v.begin(); it != v.end(); ++it) {
    if (*it == 3) {
        v.erase(it);  // it 무효화!
        // ++it;  // 위험!
    }
}

// ✅ erase 반환값 사용
for (auto it = v.begin(); it != v.end();) {
    if (*it == 3) {
        it = v.erase(it);  // 다음 반복자 반환
    } else {
        ++it;
    }
}

문제 2: end() 역참조

// ❌ end() 역참조
vector<int> v = {1, 2, 3};
auto it = v.end();
// cout << *it << endl;  // UB

// ✅ end() 체크
if (it != v.end()) {
    cout << *it << endl;
}

문제 3: 반복자 타입 불일치

// ❌ 타입 불일치
vector<int> v;
list<int> l;

// auto it = v.begin();
// it = l.begin();  // 에러

// ✅ 올바른 타입
auto vit = v.begin();
auto lit = l.begin();

iterator_traits

template<typename Iter>
void printIteratorInfo() {
    using traits = iterator_traits<Iter>;
    
    cout << "value_type: " << typeid(typename traits::value_type).name() << endl;
    cout << "difference_type: " << typeid(typename traits::difference_type).name() << endl;
    cout << "iterator_category: " << typeid(typename traits::iterator_category).name() << endl;
}

int main() {
    printIteratorInfo<vector<int>::iterator>();
}

FAQ

Q1: 반복자는 언제 사용하나요?

A:

  • 컨테이너 순회
  • STL 알고리즘
  • 범위 지정

Q2: 포인터 vs 반복자?

A: 반복자가 더 추상적이고 안전합니다.

Q3: 반복자 무효화는?

A:

  • vector: 삽입/삭제 시
  • list: 삭제된 원소만
  • map: 삭제된 원소만

Q4: 커스텀 반복자는?

A: 5가지 typedef와 연산자 구현 필요.

Q5: 반복자 성능은?

A: 인라인화로 포인터와 비슷.

Q6: 반복자 학습 리소스는?

A:

  • “Effective STL” (Scott Meyers)
  • cppreference.com
  • “The C++ Standard Library”

관련 글: 범위 기반 for, vector 완벽 가이드, size_t·ptrdiff_t, Composite 패턴.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.

  • C++ 범위 기반 for | “Range-based for” 가이드
  • C++ STL vector | “배열보다 편한” 벡터 완벽 정리 [실전 예제]
  • C++ size_t & ptrdiff_t | “크기 타입” 가이드
  • C++ Composite 패턴 완벽 가이드 | 트리 구조를 동일 인터페이스로 다루기

관련 글

  • C++ 반복자 무효화 에러 |
  • C++ 반복자 기초 완벽 가이드 | iterator 카테고리·begin/end·역방향 반복자·실전 패턴
  • C++ 커스텀 반복자 완벽 가이드 | Forward·Bidirectional
  • C++20 Ranges | begin/end 반복 탈출하고 ranges 알고리즘 쓰기
  • C++ Tag Dispatch 완벽 가이드 | 컴파일 타임 함수 선택과 STL 최적화