- 반복자는 STL을 이해하는데에 필수적인 개념이다.
- STL이 제공하는 반복자는 4가지다. (iterator, const_iterator, reverse_iterator, const_reverse_iterator)
- 이 큰장에서는 각 반복자들의 특성과, 반복자를 효율적으로 쓰는 방법을 다룰 것이다.
Contents
- 1. Item26. Prefer iterator to const_iterator, reverse_iterator, and const_reverst_iterator.
- 2. Item27. Use distance and advance to convert a container's const_iterators to iterators.
- 3. Item28. Understand how to use a reverse_iterator's base iterator
- 4. Item29. Consider istreambuf_iterators for character-by-characer input.
1.1. 각각의 반복자가 의미하는 것 ¶
- container<T>::iterator, reverse_iterator : T*
- container<T>::const_iterator, const_reverse_iterator : const T*
- reverse는 뒤에서부터 앞으로 순회함. 아닌건 앞에서부터 뒤로
1.3. 결론 ¶
- 이런 혼잡함을 겪고 싶지 않다면 그냥 딴거 쓰지 말고 iterator 쓰자는 것이다.
- 내 해석이 잘못되지 않았다면, const_iterator는 말썽의 소지가 있는 넘이라고까지 표현하고 있다.
2.1. 서론 ¶
- const_iterator는 될수 있으면 쓰지 말라고 했지만, 어쩔수 없이 써야할 경우가 있다.
- 그래서 이번 Item에서는 const_iterator -> iterator로 변환하는 법을 설명하고 있다. 반대의 경우는 암시적인 변환이 가능하지만, 이건 안된다.
- 다음엔 C++의 casting에 관한 무슨 함축적인 의미가 담긴 말을 하고 있는데.. 아시는분은 좀 가르쳐 주세요. 이런생각을 가지고 있는것에 대해 부끄러워 하라네요.
- 원문 : "When all else fails, get a bigger hammer."
- 원문 : "When all else fails, get a bigger hammer."
2.2. 잘못된 방법들 ¶
~cpp // iterator와 const_iterator를 각각 Iter, CIter로 typedef해놓았다고 하자. CIter ci; ... Iter i(ci); // 안된다. 암시적인 형변환은 성립하지 않는다. ... Iter i( const_cast<Iter>(ci) ) // 역시 안된다. vector와 string에서는 될지도 모르지만... 별루 추천하지는 않는것 같다.
- 밑에께 안되는 이유는 iterator와 const_iterator는 다른 클래스이다. 상속관계도 아닌 클래스가 형변환 될리가 없다.
- string, vector가 될수도 있는 이유
- vector<T>::iterator는 T*의 typedef, vector<T>::const_iterator는 const T*의 typedef이다. (클래스가 아니다.)
- string::iterator는 char*의 typedef, string::const_iterator는 const char*의 typedef이다. 따라서 const_cast<>가 통한다. (역시 클래스가 아니다.)
- vector<T>::iterator는 T*의 typedef, vector<T>::const_iterator는 const T*의 typedef이다. (클래스가 아니다.)
- 하지만 reverse_iterator와 const_reverse_iterator는 typedef이 아닌 클래스이다. const_cast<안된다>.
2.3. 해결책 ¶
~cpp Iter i(d.begin()); // d는 어떤 컨테이너의 인스턴스다. advance(i, distance(i,ci)); // 요렇게 하면 된다.... 인줄 알았는데 밑에 또 안된다고 써있다--;
- 왜 안되냐면, distance의 인자는 둘자 iterator다. const_iterator가 아니다.
~cpp advance(i, distance<CIter>(i,ci)); // 이렇게 하면 진짜 된다.
3.1. 예제 ¶
~cpp vector<int> v; ... // v에 차례대로 1~5까지 집어넣는다. typedef vector<int>::reverse_iterator VIRI; VIRI ri = find(v.rbegin(), v.rend(), 3); // 거꾸로 순회하면서 3을 찾는다. typedef vector<int>::iterator VIIT; VIIT i(ri.base()); // 앞에서도 말했지만 reverse 시리즈의 base()메소드를 호출해주면 그냥 시리즈로 바뀐 반복자를 리턴해준다.
rend() | ri | rbegin() | ||||
1 | 2 | 3 | 4 | 5 | ||
begin() | i | end() |
- 어째 그림이 좀 이상하긴 한데..--; 각각의 반복자가 가르키는 위치를 나타낸 것이다. 보면 알겠지만 ri에서 base()를 호출해줬는데도 가르키는게 같지가 않다.
- 결론부터 말하자면, base()메소드가 원하는 반복자를 리턴해주는건 아니다. 삽입할떄는 되지만, 원소를 지울때는 꼬인다.
- 만약에 ri가 가르키는 위치에다 새로운 원소를 삽입하고 싶다고 하자. 하지만 insert 메소드는 reverse_iterator는 인자로 받지 않는다. iterator형만 인자로 받는다. 즉 직접은 못한다는 것이다. 지울때도 이와 같은 문제가 발생한다. 그래서 base()를 쓰는 것이다.
- 99를 i가 가르키는 위치에 삽입했다고 하면(reverse시리즈는 인자로 못 넣는다.) 이렇게 될 것이다.
1 | 2 | 3 | 99 | 4 | 5 |
- 삽입은 문제없이 되었지만.. 만약에 erase()를 호출한다면? 난 3을 지우고 싶은데 base()호출해서 iterator버젼으로 넣어주면 4가 날아갈 것이다. 어떻게 해야하는가?
- 해결책은
~cpp v.erase( (++ri).base() ); // 끝. 이러면 ri는 2를 가르키게 되고 base() 호출후 리턴되는 반복자는 3을 가르킨다. 그걸 지우면 된다.