U E D R , A S I H C RSS

EffectiveSTL/Iterator

  • 반복자는 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로 암시적인 변환이 가능하다.

1.3. 결론

  • 이런 혼잡함을 겪고 싶지 않다면 그냥 딴거 쓰지 말고 iterator 쓰자는 것이다.
  • 내 해석이 잘못되지 않았다면, const_iterator는 말썽의 소지가 있는 넘이라고까지 표현하고 있다.



2. Item27. Use distance and advance to convert a container's const_iterators to iterators.

2.1. 서론

  • const_iterator는 될수 있으면 쓰지 말라고 했지만, 어쩔수 없이 써야할 경우가 있다.
  • 그래서 이번 Item에서는 const_iterator -> iterator로 변환하는 법을 설명하고 있다. 반대의 경우는 암시적인 변환이 가능하지만, 이건 안된다.
  • 다음엔 C++의 casting에 관한 무슨 함축적인 의미가 담긴 말을 하고 있는데.. 아시는분은 좀 가르쳐 주세요. 이런생각을 가지고 있는것에 대해 부끄러워 하라네요.
    • 원문 : "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<>가 통한다. (역시 클래스가 아니다.)
  • 하지만 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));     // 이렇게 하면 진짜 된다.

2.4. 잡담

  • 정말 별걸 다 설명한다는 생각이 든다. 모르고 있었으면 안쓸 것들도, 괜히 들쑤셔 내서 이거 쓰지 말아라 하니 오히려 더 헷갈린다는--;



3. Item28. Understand how to use a reverse_iterator's base iterator

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을 가르킨다. 그걸 지우면 된다.

3.2. 잡담

  • 그냥 iterator만 쓰자..--;



4. Item29. Consider istreambuf_iterators for character-by-characer input.

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:23:10
Processing time 0.0217 sec