C++ 반복자 | "Iterator" 완벽 가이드
이 글의 핵심
반복자(iterator) 는 컨테이너 요소를 순회하는 객체입니다. 범위 기반 for와 vector에서 begin/end로 쓰이며, size_t·ptrdiff_t로 거리·인덱스를 다룰 때와 Composite 패턴으로 트리 순회를 통일할 때도 자주 씁니다.
반복자 기본
반복자(iterator) 는 컨테이너 요소를 순회하는 객체입니다. 범위 기반 for와 vector에서 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]
반복자 기능 비교표
| 반복자 타입 | 읽기 | 쓰기 | 전진 | 후진 | 임의 접근 | 예시 컨테이너 |
|---|---|---|---|---|---|---|
| Input | ✅ | ❌ | ✅ | ❌ | ❌ | istream_iterator |
| Output | ❌ | ✅ | ✅ | ❌ | ❌ | ostream_iterator |
| Forward | ✅ | ✅ | ✅ | ❌ | ❌ | forward_list |
| Bidirectional | ✅ | ✅ | ✅ | ✅ | ❌ | list, set, map |
| Random Access | ✅ | ✅ | ✅ | ✅ | ✅ | vector, 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 최적화