No older revisions available
No older revisions available
- 반복자는 STL을 이해하는데에 필수적인 개념이다.
- STL이 제공하는 반복자는 4가지다. (iterator, const_iterator, reverse_iterator, const_reverse_iterator)
- 이 큰장에서는 각 반복자들의 특성과, 반복자를 효율적으로 쓰는 방법을 다룰 것이다.
1. Item26. Prefer iterator to const_iterator, reverse_iterator, and const_reverst_iterator. ¶
1.1. 각각의 반복자가 의미하는 것 ¶
- container<T>::iterator, reverse_iterator : T*
- container<T>::const_iterator, const_reverse_iterator : const T*
- reverse는 뒤에서부터 앞으로 순회함. 아닌건 앞에서부터 뒤로
1.2. iterator를 써야하는 이유 ¶
- 대부분의 메소드들의 인자가 iterator타입이다.
- 다른 iterator로부터 iterator로 암시적인 변환이 가능하다.
- 이런 혼잡함을 겪고 싶지 않다면 그냥 딴거 쓰지 말고 iterator 쓰자는 것이다.
- 내 해석이 잘못되지 않았다면, const_iterator는 말썽의 소지가 있는 넘이라고까지 표현하고 있다.
2. Item27. Use distance and advance to convert a container's const_iterators to iterators. ¶
- const_iterator는 될수 있으면 쓰지 말라고 했지만, 어쩔수 없이 써야할 경우가 있다.
- 그래서 이번 Item에서는 const_iterator -> iterator로 변환하는 법을 설명하고 있다. 반대의 경우는 암시적인 변환이 가능하지만, 이건 안된다.
- 다음엔 C++의 casting에 관한 무슨 함축적인 의미가 담긴 말을 하고 있는데.. 아시는분은 좀 가르쳐 주세요. 이런생각을 가지고 있는것에 대해 부끄러워 하라네요.
- 원문 : "When all else fails, get a bigger hammer."
~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<>가 통한다. (역시 클래스가 아니다.)
- 하지만 reverse_iterator와 const_reverse_iterator는 typedef이 아닌 클래스이다. const_cast<안된다>.
~cpp
Iter i(d.begin()); // d는 어떤 컨테이너의 인스턴스다.
advance(i, distance(i,ci)); // 요렇게 하면 된다.... 인줄 알았는데 밑에 또 안된다고 써있다--;
- 왜 안되냐면, distance의 인자는 둘자 iterator다. const_iterator가 아니다.
~cpp
advance(i, distance<CIter>(i,ci)); // 이렇게 하면 진짜 된다.
- 정말 별걸 다 설명한다는 생각이 든다. 모르고 있었으면 안쓸 것들도, 괜히 들쑤셔 내서 이거 쓰지 말아라 하니 오히려 더 헷갈린다는--;
3. Item28. Understand how to use a reverse_iterator's base iterator ¶
~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시리즈는 인자로 못 넣는다.) 이렇게 될 것이다.
- 삽입은 문제없이 되었지만.. 만약에 erase()를 호출한다면? 난 3을 지우고 싶은데 base()호출해서 iterator버젼으로 넣어주면 4가 날아갈 것이다. 어떻게 해야하는가?
- 해결책은
~cpp
v.erase( (++ri).base() ); // 끝. 이러면 ri는 2를 가르키게 되고 base() 호출후 리턴되는 반복자는 3을 가르킨다. 그걸 지우면 된다.
4. Item29. Consider istreambuf_iterators for character-by-characer input. ¶