15. 반복자

  1. 정의
  2. 특징
  3. 주의점



1. 정의

반복자(iterator)는 디자인 패턴 중 하나로, 반복자 패턴이라고도 불린다.

여기서 디자인 패턴이란, 프로그램 구성 시에 자주 사용되는 설계 유형을 뜻한다.

반복자 패턴은 각 컨테이너 별로 데이터에 접근하는 기능을 가진 iterator 객체를 제공하는 패턴으로, 컨테이너의 특성에 관계 없이 동일한 방식으로 데이터에 접근이 가능하도록 기능을 제공한다.

반복자는 STL(표준 템플릿 라이브러리)에서 컨테이너를 다룰 때 주로 쓰이며, vector 와 deque 컨테이너는 임의 접근 반복자(random access iterator)를 제공하고 나머지 모든 컨테이너는 양방향 반복자(bidirectional iterator)를 제공한다.


반복자 종류 특징
양방향 반복자(bidirectional iterator) 읽기/쓰기 모두 가능, 순/역 방향 이동(–)이 가능한 반복자
임의 접근 반복자(random access iterator) 읽기/쓰기 모두 가능, 임의 접근, 양방향 반복자 기능에 +, -, += , -=, [] 연산이 가능



2. 특징

반복자는 기본적으로 다음과 같은 기능을 가진다:

  1. 컨테이너 안의 데이터를 가리킴
  2. 가리키는 데이터에 접근
  3. 양방향 반복자의 경우 앞/뒤의 데이터로 이동 가능, 임의 접근 반복자는 추가적으로 오프셋 연산을 통해 데이터 접근 가능


주로 begin() 과 end() 함수가 각각 데이터의 처음과 끝의 반복자를 리턴해주는 함수로써 자주 사용되며, 반복자는 컨테이너의 이너 클래스로 구현되어 있다.




3. 주의점

  • end() 가 리턴하는 반복자는 마지막 데이터를 가리키지 않는다.

begin() 함수가 리턴하는 것은 첫 번째 데이터가 맞지만, end() 함수는 그렇지 않다.

end() 함수는 컨테이너의 마지막 데이터 그 다음 공간을 가리키고 있으며, 이것이 end 반복자의 특징이다.

end 반복자는 이러한 이유로 가리키는 데이터에 접근할 수 없고, 오직 해당 컨테이너의 끝임을 알리는 용도로 사용된다.


  • 반복자에 사용되는 연산자는 오버로딩 된 연산자이다.

반복자에 연산자를 지원하지만, 이는 실제 연산자가 반복자에 사용 가능한 것이 아닌 반복자 클래스 안에 오버로딩 된 연산자를 호출한다.

그 예로, 반복자는 후위 연산자 ++와 –를 연속적으로 사용할 수 없다.

실제 연산자 ++ 와 –의 후위 연산자는 연속적으로 사용해도 연산이 이루어지지만, 오버로딩된 후위 연산자는 실제 컴파일러가 후위 연산을 진행하지 않고 호출 시점에 바로 연산자를 호출하므로, 내부적으로 후위 연산 처럼 동작하게 구현한 오버로딩 연산자이다.

실제 list 컨테이너에 구현되어 있는 반복자의 ++ 후위 연산은 다음과 같이 구현되어 있다:

 
 _List_iterator operator++(int) noexcept {
     _List_iterator _Tmp = *this;
     _Mybase::operator++();
     return _Tmp;
 }


반환 값이 이터레이터이고, 레퍼런스 타입이 아니기에 연산자를 호출한 이터레이터 그 자신이 리턴되지 않으므로 연속적인 연산이 불가능하다.

아래의 예시에서 후위 연산 ++ 두번은 실제로 한번만 연산이 진행되며, 전위 연산 ++ 두번은 두 번 모두 진행되어 최종적으로 반복자는

1 -> 2 -> 4 순서로 4를 가리키게 된다.


	list<int> ml;
	ml.push_back(1);
	ml.push_back(2);
	ml.push_back(3);
	ml.push_back(4);
	ml.push_back(5);

	list<int>::iterator mi = ml.begin();
	(mi++)++;
	++(++mi);