본문 바로가기

IT책/C++14 STL 철저 입문

1.4~5 반복자

반복자는 포인터와 비슷하게 동작하는 클래스 템플릿 타입의 객체이다. iter가 클래스를 가르킨다면 iter->member 이런식으로 사용할수 있다.

 

반복자는 컨테이너 종류와 상관없이 컨테이너에 있는 원소들과 알고리즘을 이어주며 데이터에서 알고리즘을 분리시켜준다

 

반복자는 원소들을 한 쌍을 사용하는게 보통이며 범위는 begin반복자(첫번째 원소를 가르킴) ~ end반복자(마지막 원소+1위치를 가르킴)그리고 end반복자는 역참조가 될수없다.

 

반복자 유형은 선택한 컨테이너의 종류에 따라 결정되며 배열 컨테이너 또는 문자열 객체의 const반복자를 반환하는 전역 함수 cbegin()과 cend()가 있으니 기억하자(const 반복자는 변경되지 말아야 할 것을 가리키지만 반복자 자체는 바꿀 수있다.(예제 추가 예정)

 

모든 반복자 타입은 복제생성자, 복제 할당 연산자, 소멸자를 가져야 하며 반복자가 가리키는객체들은 교환(swap)할수있어야 한다.(예제 추가 예정)

 

반복자 인수의 카테고리를 두 가지 방식으로 이용가능하다 첫 번째는 연산에 필요한 최소 기능 요구조건을 만족하였는가. 두 번째는 최소 기능 요구조건이상을 만족시 알고리즘을 더 효율적으로 수행하기 위해 확장방식이다

 

(각각 어디에 쓰는지 추가 예정)

{

  • 읽기,입력 반복자는 한번 지나가면 한번 지나간곳은 다시 못돌아간다 돌아가기 위해서는 새로운 반복자를 생성해야한다.(ex : ++iter,iter++,iter1==iter2,*iter)
  • 출력 반복자는 객체에 대한 쓰기 권한을 갖는다 *iter=value라고 쓸수있다. 다시 순차열에 쓰기를 할려면 새로운 반복자를 생성해야한다(ex : ++iter, iter++,*iter)    iter->member 이런거는 불가능하다.
  • 순방향 반복자는 입력 반복자와 출력 반복자기능을 몇 번이고 사용하능하며 /범위를 검색해서 원소들을 대체하는 replace()알고리즘을 사용하려면 대체할 원소를 가리키는 반복자가 원소를 덮어쓸 때도 재사용될 수 있어야 하므로 순방향 반복자가 필요하다
  • 양방향 반복자는 순방향 반복자기능을 가지고 + 역방향으로 도 이동가능하다 (ex : --iter, iter--)
  • 랜덤 액세스 반복자는 양방향 반복자와 같은 기능에 원소들에 마음대로 접근가능하다(ex : iter + n,iter - n, iter += n,iter -= n,iter[n],*(iter+n), iter1 - iter2, iter1 < iter2)

}

 

반복자 태그 클래스의 유일한 목적은 특정 반복자 타입이 무엇을 할수있는지 지정하고 iterator템플릿 타입 인수로 사용하는 것이다.

 

 

표준 반복자 태그 클래스 목록 (예제 추가 예정)

  • input_iterator_tag
  • output_iterator_tag
  • forward_iterator_tag 은 input_iterator_tag로부터 파생
  • bidirectonal_iterator_tag는 forward_iterator_tag에서 파생
  • random_acess_iterator_tag는 bidirectional_iterator_tag에서 파생

컨테이너가 갖는 반복자의 특징은 컨테이너 타입에 따라 최소요구조건에 맞게 결정된다(vector와 deque컨테이너는 랜덤 액세스 반복자, list와 map컨테이너는 항상 양방향 반복자를 사용)

 

스트림 반복자를 사용하면 스트림과 소스 또는 스트림과 타깃 사이에 데이터를 텍스트 모드로 전송할 수 있다.

 

iostream_iterator<T>는 istream에서 타입 T의 객체를 읽을 수 있는 입력 반복자 이며 istream_iterator객체는 >>연산자로 읽게되며 읽게되는 타입은 반드시 >>연산자를 지원해야 한다. 

그리고 다양한 타입의 데이터를 전송할 때 스트림 반복자가 유일한 방법은 아니며 기본적으로 istream_iterator객체는 공백을 무시한다 이런 경우 입력스트림에 std::noskipws 조작자를 적용해서 기본동작을 덮어쓸수 있고 스트림에서 가져온 입력 객체를 다시 쓰고 싶으면 새로운 istream_iterator객체를 새로 만들어야 한다.

 

ostrea_iterator는 istream_iterator와 보완 관계이며 ostream에 객체를 위한 한 번 출력 기능을 제공하는 출력 반복자다.갹체는 << 연산자로 쓰고 ostream_iterator객체를 생성할 때 객체를 출력한 다음에 쓸 구분자 문자열을 옵션으로 지정할 수 있다

 

역방향 반복자는 표준 반복자의 반대로 동작한다. 그냥 말 그대로 다 똑같고 반대로만 동작한다. 그리고 역방향 반복자에 있는 내부 반복자(riter.base())는 역방향 반복자로 찾은 위치에 알고리즘을 적용해야 하는데 멤버함수가 역방향 반복자를 사용할수 없을때 내부 반복자를 호출해 표준 반복자를 얻을수 있다 그리고 내부 반복자 언제나 riter의 한칸 오른쪽에 있으니 명심하자

 

삽입 반복자는 표준 반복자를 기반으로 두지만 범위 안에 있는 원소에 접근만 하거나 기존 원소릴 변경만 할 수 있었다면 삽입 반복자는 컨테이너의 어느 위치든 새 원소들을 범위로 추가할 수 있다.

하지만 표준 배열이나 array<T,N>컨테이너는 원소들의 개수가 고정되어있어 삽입 반복자를 적용 할 수 없다

삽입 반복자는 세가지가 있다

 

back_insert_iterator는 push_back() 멤버 함수로 컨테이너의 끝에 새 원소를 추가하며 vector,list,deque컨테이너가 push_back()멤버를 지원한다 만약 push_back을 정의하지 않았거나 없다면 back_insert_iterator는 사용할 수 없고 전역 함수 back_inserter()는 인수로 전달된 컨테이너용 back_intsert_iterator객체를 반환한다.

(back_insert_iterator=back_inserter(arr)?) ??

 

front_insert_iterator는 push_front()멤버 함수로 컨테이너 맨 앞에 새원소를 추가하면 list,forward_list,deque컨테이너가 지원 한다. push_front()멤버가 없는 컨테이너는 front_insertr_iterator를 사용 할 수 없으며 인수로 전달된 컨테이너는 list,forward_list,deque컨테이너여야 한다

 

insert_iterator는 insert()멤버가 있는 컨테이너에 새 원소를 삽입하며 string헤더에 정의된 문자열 클래스들은 insert()멤버를 갖고 있으므로 insert_iterator객체를 사용할 수 있다. 전역 함수 inserter()는 첫 번째 인수로 지정된 컨테이너용 insert_iterator객체를 반환한다. 두번째 인수는 컨테이너에 원소를 삽입할 위치를 가리키는 반복자다

 

일반적으로 삽입 반복자는 지정된 범위에서 원소들을 복사하는 알고리즘이나 새로운 원소들을 생성하는 알고리즘에 인수로 사용된다

 

이동 반복자는 범위에서 원소 하나를 가리키며 정규 반복자에서 생성한다 이동반복자는 반복자가 가리키는 객체를 우측값을 변환하기 때문에 복제를 하지않고 이동하는 것이 가능하여 이동 반복자는 원본 범위 원소들을 정의되지 않은 상태로 남겨두게 되므로 원본 원소들을 사용하지 않아야 한다. iterator 헤더에 템플릿으로 정의된 make_move_iterator()함수에 begin()이나 end()가 반환하는 반복자를 인수로 전달해서 move_iterator를 얻을수 있다.

 

iterator헤더에는 반복자에 쓰이는 연산을 정의한 네 가지 함수 템플릿이 정의되어 있다

  • advance()는 첫 번째 인수로 받은 입력 반복자를 두 번째 인수로 지정한 숫자만큼 증가시킨다. 두 번째 인수는 첫 번째 인수에 지정된 반복자가 양방향 반복자 또는 랜덤 액세스 반복자일 때 반복자를 감소시키는 음수도 지정할 수 있다. 반환값은 없다.
int _tmain(){

	int arr[] = { 9,8,7,6,5,4,3,2,1 };
	auto iter = begin(arr);
	advance(iter, 3);
	cout << "네 번째 원소: " << *iter << endl;

}

 

  • distance()는 두 반복자 사이에 있는 원소의 갯수를 반환한다
int _tmain() 
{
	int arr[] = { 9,8,7,6,5,4,3,2,1 };

	cout << "data에 있는 원소의 개수 : " << distance(begin(arr), end(arr)) << endl;
}

 

  • next()는 첫 번째 인수로 받은 반복자를 두 번째 인수로 지정한 숫자만큼 증가시킨 반복자를 반환한다. 첫 번째 인수에는 순방향 반복자 기능이 있는 반복자만 지정가능하며 두 번째 인수는 기본값이 1로 지정되어 있다
int _tmain() 
{
	int arr[] = { 9,8,7,6,5,4,3,2,1 };

	auto iter = begin(arr);
	auto test_iter = next(iter, 3);

	cout << "iter = " << *iter << endl<< "test_iter = " << *test_iter << endl;
}

 

  • prev()는 첫 번째 인수로 받은 양방향반복자를 두 번째 인수로 지정한 숫자만큼 감소시킨 반복자를 반환한다.
int _tmain() 
{
	int arr[] = { 9,8,7,6,5,4,3,2,1 };

	auto iter = begin(arr);
	auto next_iter = next(iter, 3);
	auto prev_iter = prev(next_iter, 2);

	cout << "iter = " << *iter << endl<< "test_iter = " << *prev_iter << endl;
}

 

'IT책 > C++14 STL 철저 입문' 카테고리의 다른 글

1.8 함수를 인수로 전달하기  (0) 2019.06.27
1.7 알고리즘  (0) 2019.06.26
1.6 스마트 포인터  (0) 2019.06.25
1.3 컨테이너  (0) 2019.06.24