STL(Standard Template Library)
STL은 CPP을 위한 라이브러리로서 CPP 표준 라이브러리의 많은 부분을 영향을 끼쳤습니다.
STL은 알고리즘, 컨테이너, 반복자(iterator)로 구성되어 있습니다.
반복자 (iterator)?
STL 컨테이너에 저장된 요소를 반복적으로 순회하며 각각의 요소에 접근할 수 있게 해주는 객체입니다.
다음 코드에서 i 변수가 반복자의 역할을 하는 변수라고 볼 수 있습니다.
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << std::endl;
}
사용하는 방법은 컨테이너 타입에 iterator 키워드를 붙이면 됩니다.
예를 들어, vector라는 컨테이너의 iterator를 만들고 싶다면 다음과 같이 선언하면 됩니다.
//컨테이너<자료형>::이터레이터타입 변수
vector<int>::iterator iter;
vector<int>::const_iterator const_iter;
iterator의 경우 값에 가르키고 있는 값에 접근하여 값을 변경할 수 있지만
const_iterator 같은 경우 가르키고 있는 값에 접근은 할 수 있지만 값을 변경할수는 없습니다. (반복자 위치는 변경가능)
반복자는 다음 연산자를 사용할 수 있어야 합니다.
- 참조연산자(*) : 반복자가 가리키는 위치의 값을 접근합니다.
- 대입, 관계 연산자 : 반복자 간의 대입 연산과 비교 연산이 가능해야 합니다.
- 증감 연산자(++, --) : 반복자는 전위와 후위 연산자로 가르키는 위치를 이동할 수 있어야 합니다.
반복자의 종류
STL은 반복자의 기능에 따라 다섯가지 형태로 분류합니다.
- 입력반복자 (input iterator)
- 출력반복자 (output iteraotor)
- 순방향 반복자 (forward iterator)
- 양방향 반복자 (bidirectional iterator)
- 임의 접근 반복자 (random access iterator)
반복자는 다음과 같은 계층 구조를 가집니다.
입력반복자
입력반복자는 컨테이너로부터 값을 읽는데 사용되며, 컨테이너로부터 값을 읽을 수는 있지만 입력반복자가 가리키는 값에 접근하여 값을 변경할 수 없습니다.
입력반복자의 경우 읽기 전용 알고리즘에 사용되며 증가 연산자(++)를 사용하여 정방향으로만 이동합니다.
위의 반복자 계층구조에서 보았듯이 정방향, 양방향, 임의 접근 반복자도 입력반복자 범주에 속합니다.
기본적으로 다음과 같은 연산을 지원합니다.
복사 생성자 사용가능 | class b(a); |
복사 할당 연산자 사용가능 | class b = a; |
==, != 비교 연산자 사용가능 | a == b a != b |
역참조 가능 | *a a->data |
증가 연산자로 반복자가 가르키는 위치를 이동가능 | ++a (void)a++ *a++ |
다음 연산은 입력 반복자에서 사용이 불가능합니다.
값에 접근하여 할당 불가능 | *a = 3 // error |
감소 연산자 사용 불가능 | a-- // error |
대소 비교 연산자 사용 불가능 | a >= b // error |
산술 연산자 사용 불가능 | a - 4 // error |
출력 반복자
출력 반복자는 입력반복자와는 반대로 컨테이너의 값을 할당하는데 사용합니다.
출력 반복자는 가리키고 있는 값을 할당(변경)할 수는 있지만 값을 가져올 수는 없습니다.
출력반복자의 경우 쓰기 전용 알고리즘에서 사용되며, 증가 연산자(++)를 통해 정방향으로만 이동할 수 있습니다.
위의 반복자 계층구조에서 보았듯이 정방향, 양방향, 임의 접근 반복자도 출력반복자 범주에 속합니다.
출력반복자는 기본적으로 다음과 같은 연산을 지원합니다.
복사 생성자 사용가능 | class b(a); |
복사 할당 연산자 사용가능 | class b = a; |
역참조를 통한 값 할당가능 (한번만 가능) | *a = data; |
증가 연산자로 반복자가 가르키는 위치를 이동 가능 | ++a a++ *a++ = t |
다음 연산은 출력 반복자에서 사용이 불가능합니다.
비교 연산자 사용 불가능 | *a == *b // error |
감소 연산자 사용 불가능 | a-- // error |
값을 읽어올 수 없음 | var = *a // error |
산술 연산자 사용 불가능 | a - 4 // error |
입력 반복자와 출력 반복자는 입출력 스트림에만 적용됩니다.
정방향 반복자
정방향 반복자는 입출력이 모두 가능한 반복자로, 입력 반복자와 출력 반복자의 조합으로 간주됩니다. (입력 반복자와 출력 반복자의 기능을 모두 사용 가능함)
정방향 반복자는 다중 패스 알고리즘에 사용되며, 증가 연산자를 통하여 정방향으로만 이동할 수 있습니다.
위의 반복자 계층구조에서 보았듯이 양방향, 임의 접근 반복자도 정방향 반복자 범주에 속합니다.
정방향 반복자는 기본적으로 다음과 같은 연산을 지원합니다.
기본 생성자, 복사 생성자, 복사 할당 연산자, 소멸자 사용가능 | class a; class b(a); b = a; |
==, != 비교 연산자 사용가능 | a == b; a != b; |
역참조로 값을 읽어올 수 있음 | *a a->data |
역참조를 통한 값 할당가능 | *a = data; |
증가 연산자로 반복자가 가르키는 위치를 이동가능 | ++a a++ *a++ |
다음 연산은 정방향 반복자에서 사용이 불가능합니다.
대소 비교 연산자 사용 불가능 | a >= b // error |
감소 연산자 사용 불가능 | a-- // error |
오프셋 역참조 연산자([]) 사용불가능 | a[1] // error |
산술 연산자 사용 불가능 | a - 4 // error |
양방향 반복자
양방향 반복자는 입출력이 모두 가능한 반복자입니다.
정방향 반복자와 차이점은 양방향 반복자는 정방향은 물론 역방향으로 이동이 가능합니다.
list, map, multimap, set 및 multiset 같은 컨테이너는 양방향 반복자를 지원합니다.
위의 반복자 계층구조에서 보았듯이 임의 접근 반복자도 양방향 반복자 범주에 속합니다.
양방향 반복자는 기본적으로 다음과 같은 연산을 지원합니다.
기본 생성자, 복사 생성자, 복사 할당 연산자, 소멸자 사용가능 | class a; class b(a); b = a; |
==, != 비교 연산자 사용가능 | a == b; a != b; |
역참조로 값을 읽어올 수 있음 | *a a->data |
역참조를 통한 값 할당가능 | *a = data; |
증가 연산자로 반복자가 가르키는 위치를 이동가능 | ++a a++ *a++ |
감소 연산자로 반복자가 가르키는 위치를 이동가능 | --a a-- *a-- |
다음 연산은 양방향 반복자에서 사용이 불가능합니다.
대소 비교 연산자 사용 불가능 | a >= b // error |
오프셋 역참조 연산자([]) 사용불가능 | a[1] // error |
산술 연산자 사용 불가능 | a - 4 // error |
임의 접근 반복자
임의 접근 반복자는 반복자 중에서 가장 많은 기능을 제공하는 반복자로 양방향 반복자의 모든 기능을 포함하고 있으며 오프셋 역참조 연산자([])를 통해 임의의 위치의 값에 접근할 수 있습니다.
임의 접근 반복자는 기존의 포인터가 하는 거의 모든 일을 할 수 있습니다.
vector, deque와 같은 컨테이너는 임의 접근 반복자를 지원합니다.
양방향 반복자는 기본적으로 다음과 같은 연산을 지원합니다.
기본 생성자, 복사 생성자, 복사 할당 연산자, 소멸자 사용가능 | class a; class b(a); b = a; |
비교 연산자 사용가능 | a == b; a != b; a > b; a < b; |
역참조로 값을 읽어올 수 있음 | *a; a->data; |
역참조를 통한 값 할당가능 | *a = data; |
증가 연산자로 반복자가 가르키는 위치를 이동가능 | ++a a++ *a++ |
감소 연산자로 반복자가 가르키는 위치를 이동가능 | --a a-- *a-- |
오프셋 역참조 연산자([]) 사용불가능 | a[1] |
산술 연산자 사용 가능 (연산한 위치만큼 이동) | a + 3 |
연산자 기능별 표
반복자 기능 | 입력 반복자 | 출력 반복자 | 정방향 반복자 | 양방향 반복자 | 임의 접근 반복자 |
접근 (->) | ✔️ | ❌ | ✔️ | ✔️ | ✔️ |
읽기 (*) | ✔️ | ❌ | ✔️ | ✔️ | ✔️ |
쓰기 (*a = b) | ❌ | ✔️ (1번만 가능) |
✔️ | ✔️ | ✔️ |
비교 연산자 (==, !=) |
✔️ | ❌ | ✔️ | ✔️ | ✔️ |
증가 연산자(++) | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
감소 연산자(--) | ❌ | ❌ | ❌ | ✔️ | ✔️ |
오프셋 역참조 연산자([]) |
❌ | ❌ | ❌ | ❌ | ✔️ |
산술 연산자(+, -) | ❌ | ❌ | ❌ | ❌ | ✔️ |
산술 대입 연산자 (+=, -=) | ❌ | ❌ | ❌ | ❌ | ✔️ |
대소 비교 연산자 (<, <=, >=, >) |
❌ | ❌ | ❌ | ❌ | ✔️ |
iterator 사용예시
#include <iostream>
#include <vector>
int main () {
std::vector<int> v = {10, 20, 30, 40, 50};
std::vector<int>::iterator it;
for (it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
*it += 5;
std::cout << *it << std::endl;
}
}
위 코드를 실행시키면 다음과 같은 결과를 얻을 수 있습니다.
반복자를 사용하기 위해 벡터(컨테이너)에 대한 반복자를 다음과 같이 선언합니다.
std::vector<int>::iterator it;
그리고 begin 함수를 통해서 반복자의 시작점을 벡터의 처음 값을 가리키도록 한 후 벡터의 끝까지 접근하기위해 v.end()에 도달할 때까지 반복하도록 시킵니다.
for (it = v.begin(); it != v.end(); ++it) {
std::cout << *it << " ";
*it += 5;
std::cout << *it << std::endl;
}
여기서 알아두어야할 점은 end 함수의 경우 컨테이너의 끝 인덱스가 아닌 끝 인덱스 + 1의 값입니다. (만약 end 함수가 50의 위치를 가리키고 있으면 != 연산으로는 모든 원소를 출력할 수 없게 됩니다.)
'CPP' 카테고리의 다른 글
CPP Template (0) | 2021.12.09 |
---|---|
CPP Casts (0) | 2021.12.04 |
CPP Exception - 3 (0) | 2021.11.28 |
CPP Exception - 2 (0) | 2021.11.28 |
CPP Exception - 1 (0) | 2021.11.27 |