'std::copy'에 해당되는 글 1건

  1. 2008/03/06 귀차니스트 std::copy - ostream_iterator 사용법의 이유

  가장 맨 처음 어떤 STL Container에 들어있는 값들을 출력을 하려고 하면 흔히 사용하는 방법이 해당 Container에 속해있는 Iterator로 전진반복을 하면서 출력하는 것입니다. 보통 아래와 같은 코드가 되죠.

IterTest.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. int main( int argc, char **argv )
  5. {
  6.     std::vector< int > abc;
  7.     abc.push_back( 1 );
  8.     abc.push_back( 2 );
  9.     abc.push_back( 3 );
  10.     abc.push_back( 4 );
  11.     abc.push_back( 5 );
  12.     for( std::vector< int >::const_iterator Iter = abc.begin(); Iter != abc.end(); ++Iter ) {
  13.         std::cout << *Iter << std::endl;
  14.     }
  15.     return 0;
  16. }

  그런데 알고보면 이게 오히려 가독성을 떨어트릴 수도 있습니다. 실제로 for 구문에 대하여 모든 것을 인식해야 하고 조건, 증가식등을 알아야 하기 때문이죠. 그에 반해 좋은 방법이 하나 존재합니다. 바로 std::copy 알고리즘을 사용하는 것인데요. 사용법은 바로 아래와 같습니다.

CopyTest.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. int main( int argc, char **argv )
  5. {
  6.     std::vector< int > abc;
  7.     abc.push_back( 1 );
  8.     abc.push_back( 2 );
  9.     abc.push_back( 3 );
  10.     abc.push_back( 4 );
  11.     abc.push_back( 5 );
  12.     std::copy( abc.begin(), abc.end(), std::ostream_iterator< int >( std::cout, "\r\n" ) );
  13.     return 0;
  14. }

  이 것은 std::copy가 begin 에서 부터 end 까지 ( , ] 구간을 std::ostream_iterator 함수 객체에 대하여 = assign 연산을 수행 시켜주는 알고리즘이기 때문에 가능한 것이죠. 물론 함수객체가 아닌 함수 포인터도 가능합니다. 그런데 std::ostream_iterator 가 왜 이렇게 되는지 궁금하지 않나요? iterator 헤더 안에 들어가 검색을 해보니 다음과 같은 소스 부분이 존재하더군요.

ostream_iterator.cpp (Language : cpp)
  1. template<class _Ty,
  2.     class _Elem = char,
  3.     class _Traits = char_traits<_Elem> >
  4.     class ostream_iterator
  5.         : public _Outit
  6.     {   // wrap _Ty inserts to output stream as output iterator
  7. public:
  8.     typedef _Elem char_type;
  9.     typedef _Traits traits_type;
  10.     typedef basic_ostream<_Elem, _Traits> ostream_type;
  11. #if _SECURE_SCL
  12.     typedef _Range_checked_iterator_tag _Checked_iterator_category;
  13. #endif
  14.     ostream_iterator(ostream_type& _Ostr,
  15.         const _Elem *_Delim = 0)
  16.         : _Myostr(&_Ostr), _Mydelim(_Delim)
  17.         {   // construct from output stream and delimiter
  18.         }
  19.     ostream_iterator<_Ty, _Elem, _Traits>& operator=(const _Ty& _Val)
  20.         {   // insert value into output stream, followed by delimiter
  21.         *_Myostr << _Val;
  22.         if (_Mydelim != 0)
  23.             *_Myostr << _Mydelim;
  24. .
  25. .
  26. .
  27. 생략

  바로 선언시 전달 받은 ostream_type을 함수객체의 지역성에 의하여 보관하고 있는 상황에서 해당 객체에 대한 = operator에 의한 Assign 문장이 실행되면 *Myostr 에 << 연산으로 값을 넣게 되는 것입니다. 이 것은 미리 basic_ostream 객체( 예를 들면 cout )에서 미리 정의된 객체에 대하여 << 연산자가 미리 정의 되어있기 때문에 가능한 것이죠. 만약 미리 정의 되지 않았다면 << 연산에 대하여 컴파일 에러가 발생합니다. 이 것 뿐만이 아니라 iterator 종류는 무척 많습니다.

back_insert_iterator.cpp (Language : cpp)
  1. #include <iostream>
  2. #include <algorithm>
  3. #include <vector>
  4. int main( int argc, char **argv )
  5. {
  6.     std::vector< int > abc;
  7.     std::vector< int > def;
  8.     abc.push_back( 1 );
  9.     abc.push_back( 2 );
  10.     abc.push_back( 3 );
  11.     abc.push_back( 4 );
  12.     abc.push_back( 5 );
  13.     std::copy( abc.begin(), abc.end(), std::back_insert_iterator< std::vector< int > >( def ) );
  14.     return 0;
  15. }

  예로써 위의 back_insert_iterator 와 같은 iterator 를 들수 있습니다. 물론 이보다는 std::insert 알고리즘이 더 좋습니다. back_insert_iterator는 해당 객체에 대하여 push_back 함수를 값을 인자로 호출해주는 것을 대행하게 되죠. 이렇게 보니 이전 for 루프에 대하여 출력하기 전 보다 훨씬 가독성도 높아지고 iterator 를 재정의 하면 새로운 부분들도 확장시킬 수 있을것 같다는 생각이 들지 않나요?? Iterator 를 해당 필요상황에 맞게 잘 사용하면 무척 좋겠다는 생각이 듭니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2008/03/06 23:55 2008/03/06 23:55

댓글을 달아 주세요