* 반복자는 STL을 이해하는데에 필수적인 개념이다. * STL이 제공하는 반복자는 4가지다. (iterator, const_iterator, reverse_iterator, const_reverse_iterator) * 이 큰장에서는 각 반복자들의 특성과, 반복자를 효율적으로 쓰는 방법을 다룰 것이다. [[TableOfContents]] = Item26. Prefer iterator to const_iterator, reverse_iterator, and const_reverst_iterator. = == 각각의 반복자가 의미하는 것 == * container::iterator, reverse_iterator : T* * container::const_iterator, const_reverse_iterator : const T* * reverse는 뒤에서부터 앞으로 순회함. 아닌건 앞에서부터 뒤로 == iterator를 써야하는 이유 == * 대부분의 메소드들의 인자가 iterator타입이다. * 다른 iterator로부터 iterator로 암시적인 변환이 가능하다. == 결론 == * 이런 혼잡함을 겪고 싶지 않다면 그냥 딴거 쓰지 말고 iterator 쓰자는 것이다. * 내 해석이 잘못되지 않았다면, const_iterator는 말썽의 소지가 있는 넘이라고까지 표현하고 있다. ---- = 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(ci) ) // 역시 안된다. vector와 string에서는 될지도 모르지만... 별루 추천하지는 않는것 같다. }}} * 밑에께 안되는 이유는 iterator와 const_iterator는 다른 클래스이다. 상속관계도 아닌 클래스가 형변환 될리가 없다. * string, vector가 될수도 있는 이유 * vector::iterator는 T*의 typedef, vector::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(i,ci)); // 이렇게 하면 진짜 된다. }}} == 잡담 == * 정말 별걸 다 설명한다는 생각이 든다. 모르고 있었으면 안쓸 것들도, 괜히 들쑤셔 내서 이거 쓰지 말아라 하니 오히려 더 헷갈린다는--; ---- = Item28. Understand how to use a reverse_iterator's base iterator = == 예제 == {{{~cpp vector v; ... // v에 차례대로 1~5까지 집어넣는다. typedef vector::reverse_iterator VIRI; VIRI ri = find(v.rbegin(), v.rend(), 3); // 거꾸로 순회하면서 3을 찾는다. typedef vector::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을 가르킨다. 그걸 지우면 된다. }}} == 잡담 == * 그냥 iterator만 쓰자..--; ---- = Item29. Consider istreambuf_iterators for character-by-characer input. = ---- ["EffectiveSTL"]