Programming/C | C++

[C++] 반복자 (Iterator)

꾸준희 2018. 9. 4. 09:00
728x90
반응형

 

 

 

C++ 반복자(Iterator)

 

C++ 라이브러리는 반복자를 제공하는데 이것을 사용하면 라이브러리의 방식대로 자료구조를 액세스 할 수 있다.

따라서 라이브러리가 효과적으로 동작한다는 것을 보장 할 수 있다는 장점이 있다.

 

즉, 포인터와 상당히 비슷하며, 컨테이너에 저장되어 있는 원소들을 참조할 때 사용한다.

 

추상적으로 말하자면, 반복자란 컨테이너에 저장되어 있는 모든 원소들을 전체적으로 훑어 나갈 때 사용하는, 일종의 포인터와 비슷한 객체라고 할 수 있다. 알고리즘 마다 각기 다른 방식으로 컨테이너를 훑어가기 때문에, 반복자에도 여러가지 종류가 있게 된다. 

 

 

 

반복자의 성질

 

- 컨테이너와 컨테이너 안의 있는 요소를 구별

- 요소의 값 확인

- 컨테이너 안에 있는 요소들 간에 이동할 수 있는 연산 제공

- 컨테이너가 효과적으로 처리할 수 있는 방식으로 가용한 연산들을 한정

 

 

 

반복자 구간(range)

 

구간이란 컨테이너에 담긴 값들의 시퀀스를 나타낸다. 구간은 반복자 한 쌍으로 나타내며, 이 반복자들이 각각 시퀀스의 시작과 끝을 가리킨다. 반복자는 특정 값을 지정하는데 사용될 수 있으며, 연속적인 메모리 영역을 두 개의 포인터로 나타내듯이 한 쌍의 반복자는 값들의 구간을 설정하는데 사용될 수 있다. 

 

그러나 반복자의 경우 설정되는 범위 내의 값들이 반드시 물리적으로 연속적이어야 할 필요가 없다 .단, 이 두개의 반복자가 같은 컨테이너로부터 생성된 것이어야 하며, 두번째 반복자는 첫번째 반복자 이후에 와야한다. 그리고 첫번째 반복자에서 두번째 반복자까지 차례로 컨테이너 내부의 원소들을 처리하게 되므로 논리적인 연속성을 지닐 수 있다. 

 

또한 반복자 2개를 사용하여 컨테이너의 특정 구간에 속한 원소들을 나타내고자 한다면, 두번째 반복자가 첫번째 반복자에 도달가능 해야한다. 

 

그리고, 포인터는 널(NULL) 값을 가질 수 있는데 이는 아무것도 가리키지 않는다는 의미이다. 반복자 또한 어떤 값도 가리키지 않는 반복자를 참조하는 것은 에러를 발생시킨다. 

 

 

 

* end() 함수는 끝이 아니다

 

컨테이너를 다룰 때 자주 쓰이는 end()라는 멤버함수는 컨테이너의 맨 마지막 원소를 가리키는게 아니다. end()가 가리키고 있는 것은 맨 마지막 원소의 바로 다음번 원소이다. 따라서 이러한 반복자를 past-the-end 반복자라고 부른다. (종점을 지나쳐버린 곳을 가리키는 반복자) 

 

end() 멤버 함수를 통해 얻어지는 반복자는 결과적으로 아무 의미가 없는 것을 가리키고 있는 것이며, 이 반복자가 가리키는 것을 참조하면 예상치 못한 오류가 발생하게 된다. 

 

또한 아무 원소도 없는 컨테이너의 begin()과 end()는 같다. 

 

 

 

반복자의 종류

 

 

입력 반복자(input iterator) :  읽기만 가능, 순방향 이동, 현 위치의 원소를 한 번만 읽을 수 있는 반복자

 

출력 반복자(output iterator) : 쓰기만 가능, 순방향 이동, 현 위치의 원소를 한 번만 쓸 수 있는 반복자 

 

순방향 반복자(forward iterator) : 읽기/쓰기 모두 가능, 순방향 이동(++)이 가능한 재할당될 수 있는 반복자

 

양방향 반복자(bidirectional iterator) : 읽기/쓰기 모두 가능, 순/역 방향 이동(--)이 가능한 반복자 

 

임의 접근 반복자(random access iterator) :  읽기/쓰기 모두 가능, 임의 접근, 

                                                      양방향 반복자 기능에 +, -, += , -=, [] 연산이 가능

 

 

 

모든 컨테이너는 양방향 반복자 이상을 제공한다.

 

배열 기반 컨테이너인 vector와 deque는 임의 접근 반복자를 제공한다.

 

 

 반복자 종류 사용 방식  읽기  접근  쓰기  증감  비교 
입력 반복자
(input iterator) 
istream_iterator  =*p  -> 
++  == != 
출력 반복자
(output iterator) 
ostream_iterator
inserter
front_inserter
back_inserter 


*p=  ++ 
순방향 반복자
(forward iterator) 

=*p  ->  *p=  ++  == != 
양방향 반복자
(bidirectional iterator) 
list
set 과 multiset
map 과 multimap 
=*p  ->  *p=  ++ --  == != 
임의접근 반복자
(random access iterator) 
일반 포인터
vector
deque 
=*p  ->
[] 
*p=  ++ --
+ -
+= -= 
== !=
< >
<= >= 

 

 

추가적인 자료 : http://mayple.tistory.com/

 

 

 

 

 

 

반복자 개념을 포인터로 적용한 예제

 

정수형 배열을 가리키는 포인터 it를 선언하고 배열 첫 번째 요소의 번지에서 시작하여 끝 다음점 요소 직전까지 순회하면서 *it를 출력하면 배열 요소 전체가 출력된다. 여기서 사용된 it 포인터는 배열의 한 요소를 가리키며 증가하고 비교되며 *연산자로 요소를 읽기도 하므로 반복자의 요구 조건을 모두 만족한다. 

 

#include <iostream>  using namespace std;  void main() {       int ari[]=;       int *it;       for (it=&ari[0];it!=&ari[5];it++) {              printf("%d\n",*it);       } } 

 

 

 

 

벡터를 이용한 반복자

 

 

정수형 벡터에 1부터 5까지 정수 값을 채워넣었다. 벡터의 한 요소를 가리키는 반복자는 다음과 같이 선언한다.

 

vector<T>::iterator it;

 

vector<T>가 클래스 이름이고 이 클래스 안에 iterator라는 타입이 typedef로 정의되어 있으므로 이 타입으로 변수를 하나 선언하면 벡터의 한 요소를 가리키는 반복자가 된다. for 루프에서는 반복자를 begin으로 초기화 하고 end 직전까지 반복자를 증가시키며 벡터의 매 요소를 순회하였다. 

 

#include <iostream>  #include <vector>  using namespace std;  void main() {       int ari[]=;       vector<int> vi(&ari[0],&ari[5]);       vector<int>::iterator it;       for (it=vi.begin();it!=vi.end();it++) {            printf("%d\n",*it);       } } 

 

 

 

 

 

 

 

리스트를 이용한 반복자

 

 

이는 위 예제의 vector를 list로 바꾸었다. 벡터와 순회하는 방법이 완전 동일하다. begin ~ end 사이를 반복자가 순회하여 *it 표현식으로 순회중의 요소를 액세스 할 수 있다. 

#include <iostream>  #include <list>  using namespace std;  void main() {       int ari[]=;       list<int> li(&ari[0],&ari[5]);             list<int>::iterator it;       for (it=li.begin();it!=li.end();it++) {            printf("%d\n",*it);       } } 

 

 

 

 

벡터와 리스트의 차이

 

 

반복자가 가리키는 요소를 삭제 할 경우 그 반복자는 무효화 된다. 

 

vector에 대해 erase를 호출하면 방금 삭제된 요소 다음에 있는 요소들을 가리키는 모든 반복자는 무효화 된다.

 

또한 push_back을 사용하여 vector에 요소를 추가해도 해당 vector를 가리키고 있던 모든 반복자는 무효화 된다. 

 

vector에 한 요소를 삭제하면 그 다음 요소들이 이동되고, 한 요소를 추가하면 새로운 요소를 위한 공간을 확보하기 위해 전체 vector가 재할당되기 때문이다.

 

하지만 list에서는 erase나 push_back이 다른 요소들에 대한 반복자를 무효화 시키지 않고 실제로 삭제된 요소를 가리키는 반복자만 무효화 된다. 왜냐하면 그 요소는 더 이상 존재하지 않기 때문이다. 

 

 

 

 

 

 

 

참고자료 1 : http://egloos.zum.com/printf/v/1824410

 

참고자료 2 : http://hyeonstorage.tistory.com/318

 

참고자료 3 : http://mayple.tistory.com

 

참고자료 4 : http://egloos.zum.com/lsrsp/v/13053

 

 

 

 

 

 

728x90
반응형