반응형

출처 : [msdn]


numeric_limits::infinity 

Visual Studio 2005

The representation of positive infinity for a type, if available.

static Type infinity( ) throw( );

The representation of positive infinity for a type, if available.

The return value is meaningful only if has_infinity is true.

// numeric_limits_infinity.cpp
// compile with: /EHsc
#include <iostream>
#include <limits>

using namespace std;

int main( )
{
   cout << numeric_limits<float>::has_infinity <<endl;
   cout << numeric_limits<double>::has_infinity<<endl;
   cout << numeric_limits<long double>::has_infinity <<endl;
   cout << numeric_limits<int>::has_infinity <<endl;
   cout << numeric_limits<__int64>::has_infinity <<endl;

   cout << "The representation of infinity for type float is: "
        << numeric_limits<float>::infinity( ) <<endl;
   cout << "The representation of infinity for type double is: "
        << numeric_limits<double>::infinity( ) <<endl;
   cout << "The representation of infinity for type long double is: "
        << numeric_limits<long double>::infinity( ) <<endl;
}

Output

1
1
1
0
0
The representation of infinity for type float is: 1.#INF
The representation of infinity for type double is: 1.#INF
The representation of infinity for type long double is: 1.#INF

Header: <limits>

반응형
반응형

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

int main(){


 std::vector<int>   vecTest;
 vecTest.push_back( 10);
 vecTest.push_back( 20);
 vecTest.push_back( 30);
 vecTest.push_back( 40);
 vecTest.push_back( 50);
 std::vector<int>::iterator Ite;

 //copy( vecTest.begin(),vecTest.end(), ostream_iterator<int>() );

 Ite = lower_bound( vecTest.begin(), vecTest.end(), 3); 
 cout<<*Ite<<endl;           // 10출력

 Ite = lower_bound( vecTest.begin(), vecTest.end(), 17); 
 cout<<*Ite<<endl;           // 20출력

 Ite = lower_bound( vecTest.begin(), vecTest.end(), 20); 
 cout<<*Ite<<endl;           // 20출력

 // Ite = lower_bound( vecTest.begin(), vecTest.end(), 53); //vecTest.end() 리턴
 // cout<<*Ite<<endl;           // 에러발생 - Ite가 vecTest.end() 이라서 예외처리 해줘야한다.

 cout<<endl;

 Ite = upper_bound( vecTest.begin(), vecTest.end(), 0); 
 cout<<*Ite<<endl;     //10     


 Ite = upper_bound( vecTest.begin(), vecTest.end(), 10); 
 cout<<*Ite<<endl;      //20    

 Ite = upper_bound( vecTest.begin(), vecTest.end(), 45); 
 cout<<*Ite<<endl;          //50

 Ite = upper_bound( vecTest.begin(), vecTest.end(), 40); 
 cout<<*Ite<<endl;   //50

 //Ite = upper_bound( vecTest.begin(), vecTest.end(), 50);  //에러아님
 //cout<<*Ite<<endl;         // 에러 발생

 

 

 return 0;
}

반응형
반응형

transform(), copy() 알고리즘과 같이 연산을 하거나 단순 복사를 하는 알고리즘을 사용할 때, 결과물을 쓰기 위한 반복자(iterator)를 함수 인자로 받는다. = operator로 값을 쓰기 때문에 결과물을 특정 컨테이너 뒤에 삽입하려면 귀찮은 과정을 거쳐야 한다. 이럴 때 쓰라고 만들어 놓은 게 반복자 어댑터이다. 내부 구현을 보면 = operator 를 오버로딩해서 push_back(), push_front(), insert() 를 호출한다.



// 벡터 정의
int arr1[] = { 0, 1, 2, 3, 4 };
int arr2[] = { 5, 6, 7, 8, 9 };
 
typedef std::vector<int>    IntegerVector;
IntegerVector v1(arr1, arr1 + sizeof(arr1) / sizeof(arr1[0]));
IntegerVector v2(arr2, arr2 + sizeof(arr2) / sizeof(arr2[0]));
 
// v1 벡터의 원소를 v2 벡터에 append한다.
v2.resize(v1.size() + v2.size());
std::copy(v1.begin(), v1.end(), v2.end() - v1.size());
 
// 콘솔 출력
std::copy(v2.begin(), v2.end(), std::ostream_iterator<int>(std::cout, " "));
반복자 어댑터를 사용 안 하면 resize()와 반복자 연산을 해야 한다.




v2.reserve(v1.size() + v2.size());
std::copy(v1.begin(), v1.end(), std::back_inserter(v2));
reserve()는 원소가 많을 때 여러 번의 메모리 할당을 막으려고 호출한다. 확실히 반복자 어댑터가 간편하고 코드도 더 직관적이다. 이거 땜씨롱 반복자 어댑터를 쓰는 거지.



inserter(),back_inserter(),front_inserter() 는 각각 insert_iterator, back_insert_iterator, front_insert_iterator를 생성하는데, back_insert_iterator의 구현을 살펴보면,

template<class _Container>
class back_insert_iterator : public _Outit
{    // wrap pushes to back of container as output iterator
public:
    explicit back_insert_iterator(_Container& _Cont)
        : container(&_Cont)
    {    // construct with container
    }
 
    back_insert_iterator<_Container>& operator=(
        typename _Container::const_reference _Val)
    {    // push value into container
        container->push_back(_Val);
        return (*this);
    }
 
protected:
    _Container *container;    // pointer to container
};
= operator를 오버로딩해서 push_back을 호출한다. preincrement, postincrement, dereference 연산자는 반복자와 같은 동작을 한다.




back_inserter 

Visual Studio 2005

Creates an iterator that can insert elements at the back of a specified container.

template<class Container>
   back_insert_iterator<Container> back_inserter(
      Container& _Cont
      );

Parameters

_Cont

The container into which the back insertion is to be executed.

back_insert_iterator associated with the container object _Cont.Remarks

Within the Standard Template Library, the argument must refer to one of the three sequence containers that have the member function push_backdeque Classlist Class, or vector Class.

// iterator_back_inserter.cpp
// compile with: /EHsc
#include <iterator>
#include <vector>
#include <iostream>

int main( )
{
   using namespace std;
   int i;

   vector<int> vec;
   for ( i = 0 ; i < 3 ; ++i )  
   {
      vec.push_back ( i );
   }
   
   vector <int>::iterator vIter;
   cout << "The initial vector vec is: ( ";
   for ( vIter = vec.begin ( ) ; vIter != vec.end ( ); vIter++)
      cout << *vIter << " ";
   cout << ")." << endl;

   // Insertions can be done with template function
   back_insert_iterator<vector<int> > backiter ( vec );
   *backiter = 30;
   backiter++;
   *backiter = 40;

   // Alternatively, insertions can be done with the
   // back_insert_iterator member function
   back_inserter ( vec ) = 500;
   back_inserter ( vec ) = 600;

   cout << "After the insertions, the vector vec is: ( ";
   for ( vIter = vec.begin ( ) ; vIter != vec.end ( ); vIter++ )
      cout << *vIter << " ";
   cout << ")." << endl;
}

Output

The initial vector vec is: ( 0 1 2 ).
After the insertions, the vector vec is: ( 0 1 2 30 40 500 600 ).

Header: <iterator>

반응형
반응형

http://dev.log.mumbi.net/497


1. 알고리즘( algorithm )의 일반화.
  알고리즘의 일반화라는 것은 어떠한 컨테이너( container )도 그 알고리즘을 사용할 수 있게 만드는 것이다.

이 때, 그 알고리즘은 대상 컨테이너를 순회하기 위해서 컨테이너의 반복자를 사용한다. 보통, 시작을 가리키는 반복자와, 끝을 가리키는 반복자를 사용하고 끝을 가리키는 반복자는 순회에 포함되지 않고 컨테이너의 마지막 요소 다음을 가리킨다.

여기서 중요한 사실은 STL( Standard Template Library )이 제공하는 알고리즘이 제공하는 컨테이너 뿐 아니라 기본 배열에도 사용할 수 있게 C++ 표준이 지정되었다.

특정한 형을 사용하는 알고리즘을 일반화 시켜보면..

1. int 형 배열의 모든 원소의 합을 구하여 반환하는 함수.
 int Sum( int* _pFirst, int* _pLast )
{
    int sum = *_pFirst++;
 
    while( _pFirst != _pLast )
        sum += *_pFirst++;
 
    return sum;
}

배열의 첫 요소의 주소와 마지막 요소의 다음 주소를 인자로 받아 각 요소들을 순회하면서 합을 구한다.

2. 배열을 비롯한 모든 컨테이너에서 사용하기 위해 템플릿( template )을 적용한 일반화된 함수.
 template< class IteratorType >
 Sum( IteratorType _first, IteratorType _last )
{
    ???? sum = *_first++;
 
    while( _first != _last )
        sum += *_first++;
 
    return sum;
}

컨테이너에서는 포인터( pointer )를 일반화한 반복자( iterator )를 사용하기 때문에 시작을 가리키는 반복자와 끝을 가리키는 반복자를 인수로 받아 각 요소들을 순회하면서 합을 구한다.

그런데 요소들의 타입( type )을 모르기 때문에 그 합의 타입도 알 수 없다.

3. 반복자가 가리키는 타입 알려주는 value_type으로 일반화한 함수.
 template< class IteratorType >
typename IteratorType::value_type Sum( IteratorType _first, IteratorType _last )
{
    typename IteratorType::value_type sum = *_first++;
 
    while( _first != _last )
        sum += *_first++;
 
    return sum;
}

컨테이너의 반복자는 value_type이라는 타입을 제공한다. 반복자의 value_type은 반복자가 가리키는요소의 타입이다.

하지만 컨테이너의 반복자는 value_type을 제공하지만 기존의 포인터는 객체가 아니기 때문에 value_type을 제공하지 않으므로 배열에 대해서는 일반화되지 않았다.

배열의 포인터로 어떻게 요소의 타입을 알 수 있을까?

2. 템플릿 부분 특수화( partial specialization ).
배열의 요소 타입을 알아 내기 위해 템플릿의 부분 특수화를 알아야 한다.

[C++] 템플릿 특수화 ( template specialization )

STL 은 부분 특수화를 통해 iterator_traits 를 제공한다.

 template<class _iter="">
    struct iterator_traits      // 포인터가 아닐 경우( 반복자 ).
    {   // get traits from iterator _Iter
    typedef typename _Iter::iterator_category iterator_category;
    typedef typename _Iter::value_type value_type;      // <--
    typedef typename _Iter::difference_type difference_type;
    typedef difference_type distance_type;  // retained
    typedef typename _Iter::pointer pointer;
    typedef typename _Iter::reference reference;
    };
 
template<class _ty="">
    struct iterator_traits<_Ty *> // 포인터일 경우.
    {   // get traits from pointer
    typedef random_access_iterator_tag iterator_category;
    typedef _Ty value_type;         // <--
    typedef ptrdiff_t difference_type;
    typedef ptrdiff_t distance_type;    // retained
    typedef _Ty *pointer;
    typedef _Ty& reference;
    };

 

위의 코드는 MSVC9 에서 제공하는 STL의 iterator_traits 이다.

부분 특수화를 이용해 포인터일 경우와 반복자일 경우의 value_type을 다르게 하여 제공한다.

3. iterator_traits.
결론적으로 iterator_traits 는 반복자 특질이라고 번역되지만 사실, 반복자와 배열의 포인터를 구분하기 위해서 만들어진 것이다.

알고리즘을 구현하고 있다면 iterator_traits 가 무엇인지 반드시 알아야 한다.


iterator_traits Class 

Visual Studio 2005

A template helper class used to specify all the critical type definitions that an iterator should have.

template<class Iterator>
   struct iterator_traits {
   typedef typename Iterator::iterator_category iterator_category;
   typedef typename Iterator::value_type value_type;
   typedef typename Iterator::difference_type difference_type;
   typedef typename Iterator::pointer pointer;
   typedef typename Iterator::reference reference;
   };
template<class Type>
   struct iterator_traits<Type*> {
   typedef random_access_iterator_tag iterator_category;
   typedef Type value_type;
   typedef ptrdiff_t difference_type;
   typedef Type *pointer;
   typedef Type& reference;
   };
template<class Type>
   struct iterator_traits<const Type*> {
   typedef random_access_iterator_tag iterator_category;
   typedef Type value_type;
   typedef ptrdiff_t difference_type;
   typedef const Type *pointer;
   typedef const Type& reference;
   };

The template class defines the member types

  • iterator_category: a synonym for Iterator::iterator_category.

  • value_type: a synonym for Iterator::value_type.

  • difference_type: a synonym for Iterator::difference_type.

  • pointer: a synonym for Iterator::pointer.

  • reference: a synonym for Iterator::reference.

The partial specializations determine the critical types associated with an object pointer of type Type * or const Type *.

In this implementation you can also use several template functions that do not make use of partial specialization:

template<class Category, class Type, class Diff>
C _Iter_cat(const iterator<Category, Ty, Diff>&);
template<class Ty>
    random_access_iterator_tag _Iter_cat(const Ty *);

template<class Category, class Ty, class Diff>
Ty *_Val_type(const iterator<Category, Ty, Diff>&);
template<class Ty>
    Ty *_Val_type(const Ty *);

template<class Category, class Ty, class Diff>
Diff *_Dist_type(const iterator<Category, Ty, Diff>&);
template<class Ty>
    ptrdiff_t *_Dist_type(const Ty *);

which determine several of the same types more indirectly. You use these functions as arguments on a function call. Their sole purpose is to supply a useful template class parameter to the called function.

// iterator_traits.cpp
// compile with: /EHsc
#include <iostream>
#include <iterator>
#include <vector>
#include <list>

using namespace std;

template< class it >
void
function( it i1, it i2 )
{
   iterator_traits<it>::iterator_category cat;
   cout << typeid( cat ).name( ) << endl;
   while ( i1 != i2 )
   {
      iterator_traits<it>::value_type x;
      x = *i1;
      cout << x << " ";
      i1++;
   };   
   cout << endl;
};

int main( ) 
{
   vector<char> vc( 10,'a' );
   list<int> li( 10 );
   function( vc.begin( ), vc.end( ) );
   function( li.begin( ), li.end( ) );
}

Output

struct std::random_access_iterator_tag
a a a a a a a a a a 
struct std::bidirectional_iterator_tag
0 0 0 0 0 0 0 0 0 0 

Header: <iterator>

반응형
반응형

http://blog.daum.net/coolprogramming/93

 

4, 삽입 반복자

삽입 반복자(insert iterator)는 알고리즘에서 반복자가 가리키는 위치에 값을 "덮어"(overwrite)쓰지 않고 "삽입"(insert)할 수 있도록 바꿔주는 반복자입니다.

STL은 3 가지의 삽입 반복자를 제공합니다.

  • back_insert_iterator : push_back 멤버 함수를 사용하여 삽입

    • 시퀀스 컨테이너(string, vector, list, deque)는 push_back 멤버 함수를 제공하므로 사용 가능
  • front_insert_iterator : push_front 멤버 함수를 사용하여 삽입

    • list와 deque만 push_front 멤버 함수를 제공하므로 사용 가능
  • insert_iterator : insert 멤버 함수를 사용하여 삽입

    • 모든 컨테이너는 insert 멤버 함수를 제공하므로 사용 가능

반복자를 이용한 덮어쓰기와 삽입을 그림으로 보입니다.

1) 덮어쓰기 모드

insert_iterator(1).png

2) 삽입 모드

insert_iterator2.png

일반적으로 알고리즘은 덥어쓰기로 동작하므로 알고리즘을 사용하기 전에 저장할 원소의 size가 존재해야 합니다.

간단한 copy 알고리즘을 예로 사용합니다.

#include <iostream>

#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
vector<int> v1;
vector<intv2(3);


v1.push_back(10);

v1.push_back(20);
v1.push_back(30);

copy(v1.begin(), v1.end(), v2.begin());

forint i = 0 ; i < v2.size() ; i++)
cout << v2[i] << " ";
cout << endl;
}

  1. 10 20 30

v1.begin()~v2.end()까지의 원소를 모두 v2에 저장합니다. 이때 v2는 저장할 개수만큼의 size를 가지고 있어야합니다. 여기서는 3개입니다.

만약 size를 가지고 있지 않다면 실행 오류가 발생합니다.

v2의 size는 0 입니다.

#include <iostream>

#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
vector<int> v1;
vector<intv2;


v1.push_back(10);

v1.push_back(20);
v1.push_back(30);

copy(v1.begin(), v1.end(), v2.begin());

forint i = 0 ; i < v2.size() ; i++)
cout << v2[i] << " ";
cout << endl;
}

  1. 안녕~~!

v2의 size가 0이므로 copy 알고리즘에서 실행 오류가 발생합니다.

이것은 해결하려면 copy 알고리즘에 삽입 반복자를 전달하면 됩니다.

insert_iterator를 사용한 예제입니다.

#include <iostream>

#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
vector<int> v1;
vector<int> v2;


v1.push_back(10);

v1.push_back(20);
v1.push_back(30);

copy(v1.begin(), v1.end(), inserter< vector<int> >(v2, v2.begin()) );

forint i = 0 ; i < v2.size() ; i++)
cout << v2[i] << " ";
cout << endl;
}

  1. 10 20 30

inserter는 insert_iterator를 생성합니다. 이 삽입 반복자는 insert() 멤버 함수를 사용하여 삽입니다.

inserter는 템플릿 함수로 insert_iterator를 반환합니다. 첫 번째 인자에 컨테이너를 두 번째 인자에 삽입할 위치의 반복자를 지정하면 됩니다.

또 다른 예제입니다.

#include <iostream>

#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
vector<int> v1;
vector<int> v2;


v1.push_back(10);

v1.push_back(20);
v1.push_back(30);


v2.push_back(100);

v2.push_back(200);
v2.push_back(300);

copy(v1.begin(), v1.end(), inserter< vector<int> >(v2, v2.begin() ) );

forint i = 0 ; i < v2.size() ; i++)
cout << v2[i] << " ";
cout << endl;
}

  1. 10 20 30 100 200 300

v2가 3개의 원소(100,200,300)를 가지고 있고 v2.begin()에 삽입 위치를 지정했으므로 앞쪽에 삽입됩니다.

back_insert_iterator를 사용한 예제입니다.

#include <iostream>

#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
vector<int> v1;
vector<int> v2;

v1.push_back(10);

v1.push_back(20);
v1.push_back(30);

v2.push_back(100);

v2.push_back(200);
v2.push_back(300);

copy(v1.begin(), v1.end(), back_inserter< vector<int> >(v2) );

forint i = 0 ; i < v2.size() ; i++)
cout << v2[i] << " ";
cout << endl;
}

  1. 100 200 300 10 20 30

back_inserter는 back_insert_iterator를 생성합니다. 이 삽입 반복자는 push_back() 멤버 함수를 사용하여 삽입(추가)합니다.

back_inserter는 템플릿 함수로 back_insert_iterator를 반환합니다. 함수의 인자에 컨테이너 지정하면 됩니다.

vector는 push_front 멤버 함수가 없으므로 front_insert_iterator는 사용할 수 없습니다.

#include <iostream>

#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
vector<int> v1;
vector<int> v2;

v1.push_back(10);

v1.push_back(20);
v1.push_back(30);

v2.push_back(100);

v2.push_back(200);
v2.push_back(300);

copy(v1.begin(), v1.end(), front_inserter< vector<int> >(v2) );

forint i = 0 ; i < v2.size() ; i++)
cout << v2[i] << " ";
cout << endl;
}

  1. 안녕~~!

에러입니다. front_inserter는 템플릿 함수로 front_insert_iterator를 반환하지만 vector는 push_front() 멤버 함수가 없으므로 front_insert_iterator를 사용할 수 없습니다.

list는 push_front 멤버 함수를 가지고 있으므로 front_insert_iterator를 사용할 수 있습니다.

#include <iostream>
#include <algorithm>
#include <list>
using namespace std;

void main( )
{
list<int> lt1;
list<int> lt2;

lt1.push_back(10);
lt1.push_back(20);
lt1.push_back(30);

lt2.push_back(100);

lt2.push_back(200);
lt2.push_back(300);

copy(lt1.begin(), lt1.end(), front_inserter< list<int> >(lt2) );

list<int>::iterator iter;
for( iter = lt2.begin(); iter != lt2.end() ; iter++)
cout << *iter << " ";
cout << endl;
}

  1. 30 20 10 100 200 300

push_front 멤버 함수를 사용하여 정수를 삽입합니다.

5, 입,출력 스트림 반복자

입,출력 스트림 반복자(input,output stream iterator)는 I/O 스트림에 연결된 조금 특별한 반복자입니다. 알고리즘에서 스트림 입출력을 쉽게 수행할 수 있도록 제공합니다. 입력 스트림 반복자는 istream_iterator 클래스를 출력 스트림 반복자는 ostream_iterator 클래스를 사용합니다.

copy 알고리즘에서 출력 스트림 반복자를 사용한 예제입니다.

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
vector<int> v;

v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);

copy(v.begin(), v.end(), ostream_iteratorint >(cout, " " ) );
cout<< endl;

copy(v.begin(), v.end(), ostream_iteratorint >(cout, ", " ) );
cout<< endl;

}

  1. 10 20 30 40 50
    10, 20, 30, 40, 50,

ostream_iterator는 템플릿 클래스로 출력 반복자를 추상화한 클래스입니다. 템플릿 인자는 출력 타입이며(int) 생성자의 첫 번째 인자는 출력 스트림 객체, 두 번째 인자는 출력 값들 사이에 출력될 문자열입니다.

다음은 istream_iterator의 간단한 예제입니다.

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;


void main( )
{
vector<int> v;

v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);

copy( istream_iterator<int>(cin), istream_iterator<int>(), ostream_iterator< int >(cout, "," ) );
cout<< endl;
}

  1. 10 20 30 40 50^Z
    10,20,30,40,50,

예제는 입력 스트림 객체(cin)에서 입력되는 정수들을 스트림 끝까지 입력 받아 출력하는 예제입니다.

istream_iterator<int>( cin )은 cin(입력 스트림 객체)을 통해 입력되는 정수 값을 입력 받는 반복자를 istream_iterator<int>( )은 입력 스트림 반복자의 끝 표시기(end marker)를 역할을 하는 입력 반복자를 의미합니다.

여기까지 C++, STL QS1을 모두 마무리합니다. ㅠ.ㅠ...

10개월 동안 해오던 일을 마무리하니 시원함? 아쉬움? 뿌듯함? 허탈함?이 모두 공존합니다.

아직 못 다한 고급(?)적인 내용은 C++, STL QS2에서 공부합니다. QS2는 조금 어려운 내용이 될 수도...

수고하셨습니다. 꾸벅!

반응형
반응형

http://cmlife.blogspot.com/2007_02_01_archive.html


auto_ptr

책: C++ Standard Library 튜토리얼.레퍼런스
(니콜라이 M.조슈티스 저 / 문정환.김희준 공역 )
68p
auto_ptr 클래스

일반적으로 사용자는 포인터를 사용할 경우, 명시적으로 리소스를 관리해 주어야 한다.
void foo()
{
classA * ptr = new ClassA; // 생성..
... 작업수행..
delete ptr; // 파괴
}
이 함수에는 문제가 발생할 여지가 있다.
객체를 파괴하는 작업을 잊을수도 있고..
예외가 발생할 경우에는 delete문장이 호출되지 않고 곧바로 함수가 종료되어..메모리 릭이 발생할 수 있다.
그래서 예외처리를 위해서 try {... } catch() {....}로 처리해야 한다
만약 여러개의 객체일 경우 더욱 복잡해져 버리게 된다.
스마트 포인터는 이런 경우에 도움이 될수 있다. 스마트 포인터는 포인터 자체가 파괴될때 가리키고 있던 리소스를 항상 해제할 수 있다. 게다가 정상적이던 예외로이던 함수가 종료될때 포인터는 자동으로 파괴된다.

70p
auto_ptr은 자신이 가리키는 객체에 대한 소유자로서 행동하는 포인터이다. 결과적으로 auto_ptr이 파괴되면 소유하던 객체도 자동으로 파괴되는 것이다. auto_ptr의 요구사항은, 객체는 단 하나의 소유자만을 가질수 있다는 것이다.

#include // auto_ptr을 사용하기 위한 헤더
using namespace std;

void foo()
{
auto_ptr ptr( new ClassA );
......작업수행..
}

delete와 예외처리를 위한 문장이 더이상 필요하지 않다.
auto_ptr은 통상적인 포인터와 같은 인터페이스를 가지고 있다.
'*'연산자와, '->"연산자를 사용할수 있으며 클래스나 구조체일 경우 멤버에 대한 억세스를 제공한다.
그러나 ++/--같은 포인터 증감 연산은 사용불가하다..(Undefined)

auto_ptr<>클래스는 대입연산자로(=)로 초기화할 수 없다.
반드시 생성자를 사용하여 초기화를 해야 한다.

auto_ptr ptr1( new ClassA ); // OK!!
auto_ptr ptr2 = new ClassA; // ERROR !!!!!!!!!!!!!!

또는
auto_ptr ptr3; // auto_ptr이 생성된다.
ptr3 = auto_ptr(new ClassA); // 생성자를 이용해 명시적으로 생성한다.
ptr3이 가진 소유권이 파괴된후.. 새로 생성한 ( ...= auto_ptr(new ClassA) ) 것으로 소유권을 이전 받는다.

71p
auto_ptr에 의한 소유권 이전

auto_ptr ptr1( new ClassA );
auto_ptr ptr2( ptr1 );


이 경우에.. ptr1이 가지고 있던 소유권이 ptr2로 이전되고..
ptr1은 NULL 포인터를 가지게 된다...
그래서 ptr1은 이제 더이상 사용되서는 안된다.(이것은 프로그래머의 책임.)

auto_ptr ptr1( new ClassA );
auto_ptr ptr2;
ptr2 = ptr1;
이 경우에도 ptr1의 소유권이 ptr2로 넘어간다.


auto_ptr ptr1( new ClassA );
auto_ptr ptr2( new ClassA );
ptr2 = ptr1;

ptr1, ptr2모두 각각 다른 객체에 대한 소유권을 가지고 있을때는 어떻게 처리될까..
이때는 ptr2 = ptr1시에... ptr2가 가진 객체의 소유권이 파괴가 된다..
그런후 ptr1의 소유권을 이전받는 것이다.

위와 같은 소유권의 이전은 함수에서도 똑같이 적용된다.
auto_ptr foo()
{
auto_ptr ptr
....
return ptr;
}

void g()
{
auto_ptr p;

p = foo(); // 소유권을 이전 받는다.
}


auto_ptr을 인자로 사용할 경우 문제가 생긴다.

auto_ptr p( new int );

*p = 42; // *p에 값을 대입할수 있다. 정상.
bad_print( p ); // 소유권 이전이 일어난다.
// p가 가진 소유권이 bad_print()함수로 넘어가고 p는 NULL이 되어버렸다.
*p = 34; // 런타임 에러가 발생한다.

그럼 레퍼런스를 사용하면 되지 않을까 생각할수 있지만..
레퍼런스로 전달하면 함수는 소유권을 이전받았을 수도 있고 아닐 수도 있다.
auto_ptr을 레퍼런스타입으로 전달하는 것은 반드시 피해야 한다.

auto_ptr을 좀더 안전하게 만드는 방법으로 결정된 것은..const를 붙이는 것이다.
const auto_ptr p( new int );
*p = 42;
bad_print( p ); // 컴파일 에러.
*p = 34;
return p; // 컴파일 에러

const auto_ptr을 사용하면 의도하지 않은 소유권의 이전을 막을수 있다.
const는 auto_ptr객체의 값을 변경할수 없다는 뜻이 아니다...,
소유권을 이전할수 없다는 것을 의미한다.


(76p)
멤버 변수로의 auto_ptr
클래스 안에서 auto_ptr을 사용하게 되면 리소스 릭을 피할수 있다. 또한 파괴자가 필요없게 된다.
객체의 생성시 예외가 발생해도 리소스 릭을 피할수 있는 것이다.
한가지 주목해야 할 점은 파괴자는 생략하더라도, 복사 생성자와 대입연산자는 반드시 작성해야 한다.


(78p)
잘못된 사용례.

auto_ptr은 배열을 참조하는 것을 허락하지 않는다. 이것은 auto_ptr이 자신이 소유한 객체를 파괴하기 위해서 delete[]가 아닌 delete를 호출하기 때문이다. C++표준 라이브러리에서는 배열타입을 위한 auto_ptr은 없다. 대신 라이브러리는 데이터의 집합을 관리하기 위해서 컨테이너 클래스들을 제공한다.

auto_ptr은 표준 컨테이너 클래스와 함께 사용될 수 없다.
auto_ptr은 값의 복사가 아닌 소유권의 이전이 이루어지므로 그렇다.
다행히 라이브러리는 이러한 실수에 대해 컴파일에러를 제공한다. :)

(79p)
const auto_ptr 사용예..

#include #include 
template ostream & operator<<( ostream &os, const auto_ptr & p ){ if(p.get()==NULL) { os << "NULL"; } else { os << *p; } return os; } templatevoid Func( const auto_ptr & p ){ *p += 1;
cout << "Func(+1시킴) : " <<> p (new int(42)); auto_ptr q;

cout << "after initialization:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl;
Func(p);
q = p; cout << "\nafter assign:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl;
*q += 13; p = q; cout << "\nafter change:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl;
cin.get();
}

반응형
반응형


 http://blog.naver.com/uleena?Redirect=Log&logNo=80042072372




#include <iostream>

#include <string>

#include <functional>

using namespace std;

 

#include <ctime>

#include <bitset>

 

// unique 한랜덤을만들어내는함수객체

template<int MAX> class URandom

{

        bitset<MAX> bs;

        bool recycle;

public:

        URandom(bool b = false) : recycle(b)

        {

               srand(time(0)); // 난수초기화

               bs.set();       // 모든비트를1

        }

 

        int operator()()

        {

               if ( bs.none() )   // 모두0인경우

               {

                       if ( recycle == false// 재사용안하는경우

                              return -1;

                       else

                              bs.set();  // 모두1

               }

 

               int n = -1;

               while ( !bs.test( n = (rand() % MAX) ));

               bs.reset(n);

               return n;

        }

};

 

int main()

{

        URandom<5> r;

 

        for ( int i = 0; i < 15;++i)

               cout << r() << endl;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

// 3. bitset : 비트의집합

#include <bitset>

 

int main()

{

        bitset<10> bs;

        bs.reset(); // 모두0

 

        // 각비트를조작하는법

        bs.set(4);

        bs[5] = true;

        bs[6].flip();

 

        // 비트조사

        if ( bs.test(4) ) cout << "비트4set" << endl;

        if ( bs[5]      ) cout << "비트5set" << endl;

 

        unsigned long n = bs.to_ulong();

        cout << n << endl;

 

        string s = bs.to_string();

        cout << s << endl;

}

 

 

       

 

 

 

 

// 2. set : 순서를가지고요소를저장한다. (대부분의STL구현물은RB-Tree사용)

 

#include <set>

 

int main()

{

        typedef set<int, greater<int>> SET;

 

        //set< int, greater<int> > s;

        SET s;

       

        s.insert(10);

        s.insert( 5);

        s.insert(15);

        s.insert(30);

        s.insert(4);

 

        pair< SET::iterator, bool> ret = s.insert(10); // 중복허용될까?

 

        if ( ret.second == false )

               cout << "이미요소가있습니다." << endl;

 

 

        ostream_iterator<int> os(cout, " ");

 

        copy( s.begin(), s.end(), os);

}

 

 

 

 

 

 

 

 

 

 

// 1. pair의개념

 

 

template<typename T1, typename T2> struct pair

{

        T1 first;

        T2 second;

 

        pair(T1 a = T1(), T2 b = T2() ) :first(a), second(b) {}

};

 

pair<intint> foo()

{

        // 이안에서2개의값을리턴해주고싶다.

}

 

 

int main()

{

        // TR1(또는boost)에는pair에개선된버전인tuple이있다.

        tuple<> none;

        tuple<int> one;

        tuple<intint> two;

        tuple<intintint> three;

        tuple<intintintint> n;

 

 

 

        pair<int, pair<intint>> p2;

 

        pair<intdouble> p1(1,3.4);

 

        cout << p1.second << endl; // ?

}

반응형
반응형

typename 을 써야 하는 순간

 

http://blog.naver.com/spinx85?Redirect=Log&logNo=140112304004

 

 

 

 

http://dev.log.mumbi.net/497

[C++] iterator_traits( 반복자 특질 )이란 무엇인가?

Language/C++ | 2010/01/28 16:56 | Posted by 임준환( 멈비 )

1. 알고리즘( algorithm )의 일반화.

  알고리즘의 일반화라는 것은 어떠한 컨테이너( container )도 그 알고리즘을 사용할 수 있게 만드는 것이다.

이 때, 그 알고리즘은 대상 컨테이너를 순회하기 위해서 컨테이너의 반복자를 사용한다. 보통, 시작을 가리키는 반복자와, 끝을 가리키는 반복자를 사용하고 끝을 가리키는 반복자는 순회에 포함되지 않고 컨테이너의 마지막 요소 다음을 가리킨다.

여기서 중요한 사실은 STL( Standard Template Library )이 제공하는 알고리즘이 제공하는 컨테이너 뿐 아니라 기본 배열에도 사용할 수 있게 C++ 표준이 지정되었다.

특정한 형을 사용하는 알고리즘을 일반화 시켜보면..

1. int 형 배열의 모든 원소의 합을 구하여 반환하는 함수.

1
2
3
4
5
6
7
8
9
int Sum( int* _pFirst, int* _pLast )
{
    int sum = *_pFirst++;
  
    while( _pFirst != _pLast )
        sum += *_pFirst++;
  
    return sum;
}

배열의 첫 요소의 주소와 마지막 요소의 다음 주소를 인자로 받아 각 요소들을 순회하면서 합을 구한다.

2. 배열을 비롯한 모든 컨테이너에서 사용하기 위해 템플릿( template )을 적용한 일반화된 함수.

1
2
3
4
5
6
7
8
9
10
templateclass IteratorType >
???? Sum( IteratorType _first, IteratorType _last )
{
    ???? sum = *_first++;
  
    while( _first != _last )
        sum += *_first++;
  
    return sum;
}

컨테이너에서는 포인터( pointer )를 일반화한 반복자( iterator )를 사용하기 때문에 시작을 가리키는 반복자와 끝을 가리키는 반복자를 인수로 받아 각 요소들을 순회하면서 합을 구한다.

그런데 요소들의 타입( type )을 모르기 때문에 그 합의 타입도 알 수 없다.

3. 반복자가 가리키는 타입 알려주는 value_type으로 일반화한 함수.

1
2
3
4
5
6
7
8
9
10
templateclass IteratorType >
typename IteratorType::value_type Sum( IteratorType _first, IteratorType _last )
{
    typename IteratorType::value_type sum = *_first++;
  
    while( _first != _last )
        sum += *_first++;
  
    return sum;
}

컨테이너의 반복자는 value_type이라는 타입을 제공한다. 반복자의 value_type은 반복자가 가리키는 요소의 타입이다.

하지만 컨테이너의 반복자는 value_type을 제공하지만 기존의 포인터는 객체가 아니기 때문에 value_type을 제공하지 않으므로 배열에 대해서는 일반화되지 않았다.

배열의 포인터로 어떻게 요소의 타입을 알 수 있을까?

2. 템플릿 부분 특수화( partial specialization ).

배열의 요소 타입을 알아 내기 위해 템플릿의 부분 특수화를 알아야 한다.

[C++] 템플릿 특수화 ( template specialization ) 

STL 은 부분 특수화를 통해 iterator_traits 를 제공한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<CLASS _iter="">
    struct iterator_traits      // 포인터가 아닐 경우( 반복자 ).
    {   // get traits from iterator _Iter
    typedef typename _Iter::iterator_category iterator_category;
    typedef typename _Iter::value_type value_type;      // <--
    typedef typename _Iter::difference_type difference_type;
    typedef difference_type distance_type;  // retained
    typedef typename _Iter::pointer pointer;
    typedef typename _Iter::reference reference;
    };
  
template<CLASS _ty="">
    struct iterator_traits<_Ty *> // 포인터일 경우.
    {   // get traits from pointer
    typedef random_access_iterator_tag iterator_category;
    typedef _Ty value_type;         // <--
    typedef ptrdiff_t difference_type;
    typedef ptrdiff_t distance_type;    // retained
    typedef _Ty *pointer;
    typedef _Ty& reference;
    };</CLASS></CLASS>

위의 코드는 MSVC9 에서 제공하는 STL의 iterator_traits 이다.

부분 특수화를 이용해 포인터일 경우와 반복자일 경우의 value_type을 다르게 하여 제공한다.

3. iterator_traits.

결론적으로 iterator_traits 는 반복자 특질이라고 번역되지만 사실, 반복자와 배열의 포인터를 구분하기 위해서 만들어진 것이다.

알고리즘을 구현하고 있다면 iterator_traits 가 무엇인지 반드시 알아야 한다.

반응형
반응형


http://www.viper.pe.kr/cgi-bin/moin.cgi/STL/%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88

1. 컨테이너(Container) [Bottom] [Top]

1.1. 컨테이너의 특징 [Bottom] [Top]

  • 특징

    vector

    deque

    list

    set/multiset

    map/multimap

    내부 자료구조

    동적 배열

    배열의 배열

    이중 링크드 리스트

    바이너리 트리

    바이너리 트리

    원소

    value

    value

    value

    value

    Key/value

    중복 허옹 여부

    허용

    허용

    허용

    허용 안함 / 허용

    key에 대해서만 허용 안함 / 허용

    랜덤 액세스 여부

    허용

    허용

    허용 안함

    허용 안함

    key를 사용하여 가능 / 허용 안함

    반복자 카테고리

    랜덤 액세스

    랜덤 액세스

    양방향

    양방향(원소는 상수이다)

    양방향(key는 상수)

    원소의 검색/찾기

    느림

    느림

    매우 느림

    빠름

    key에 대해서 빠름

    원소의 삽입 및 제거가 
    빠른 구간

    컨테이너의 끝

    컨테이너의 앞과 끝

    아무곳에서나

    -

    -

    삽입/제거 동작의 반복자와 
    레퍼런스, 포인트의 무효화

    재할당시

    항상

    일어나지 않음

    일어나지 않음

    일어나지 않음

    제거된 원소에 대한 메모리 
    해제 여부

    일어나지 않음

    가끔 일어남

    항상 일어남

    항상 일어남

    항상 일어남

    메모리 예약의 허용 여부

    허용

    허용 안함

    -

    -

    -

    트랜젝션 안전성(성공하거나 
    아무런 영향도 없다)

    push, pop

    push, pop

    sort()와 할당 연산자를 
    제외한 모든 연산들

    다중 원소 삽입 제외한 
    모든 연산

    다중 원소 삽입 제외한 
    모든 연산

1.2. 시퀀스 컨테이너(Sequence container) [Bottom] [Top]

  • vector - 가변 길이 시퀀스, 임의 접근(random access) 가능, 시퀀스의 끝에서 삽입/삭제.
  • deque - 가변 길이 시퀀스, 임의 접근 가능, 시퀀스의 앞, 끝에서 삽입/삭제.
  • list - 가변 길이 시퀀스, 선형 시간 접근만 가능, 임의의 위치에 삽입/삭제 가능.

1.3. 정렬 연관 컨테이너(Sorted associative container) [Bottom] [Top]

  • set - 유일 키(Unique key) 지원, 빠른 키(Key) 값 검색.
  • multiset - 중복 키(Duplicate key) 지원, 빠른 키 값 검색.
  • map - 유일 키 지원, 키 값으로 데이터 검색.
  • multimap - 중복 키 지원, 키 값으로 데이터 검색.

1.4. 컨테이너 어댑터(Container adaptor) [Bottom] [Top]

  • stack - vector, deque, list 에 적용 가능, LIFO(후입선출) 구조.
  • queue - deque, list 에 적용 가능, FIFO(선입선출) 구조.
  • priority_queue - vector, deque 에 적용 가능, 비교 함수 객체에 따라 우선 순위 처리.

1.5. 비표준 컨테이너(Non-standard container) [Bottom] [Top]

  • hash_map
  • hash_set
  • bitset

2. 컨테이너 동작 [Bottom] [Top]

주요 컨테이너의 기본 동작에 대하여 그림으로 정리

참조> 그림을 보는 방법

  • 구분

    설명

    초록색 (Green)

    컨테이너

    파란색 (Blue)

    참조 함수

    보라색 (Purple)

    삽입/삭제 함수

2.1. vector 동작 [Bottom] [Top]

2.2. queue 동작 [Bottom] [Top]

2.3. deque, list 동작 [Bottom] [Top]

2.4. stack 동작 [Bottom] [Top]

3. 컨테이너 함수 [Bottom] [Top]

3.1. insert() 함수 [Bottom] [Top]

  • Toggle line numbers
       1 // 클래스 선언
       2 class CData
       3 {
       4         ...
       5 };
       6 
       7 // 데이터형 선언
       8 typedef std::set< CData * >                     SET_DATA;
       9 typedef std::pair< SET_DATA::iterator, bool >   SET_DATA_INSERT_RESULT;
      10 
      11 
      12 //------------------------------------------------------------------------------
      13 // 컨테이너 정의
      14 SET_DATA setData;
      15 
      16 // 새로운 객체 할당
      17 CData * pData = new CData();
      18 
      19 // 데이터 삽입 & 결과
      20 SET_DATA_INSERT_RESULT result = setData.insert( pData );
      21 if( false == result.second )
      22 {
      23         // ERROR: 동일한 데이타 존재함
      24 }
    


UnfinishedPage

반응형
반응형

http://blog.naver.com/infopub?Redirect=Log&logNo=100024418644

반응형
반응형

http://blog.daum.net/coolprogramming/90

 1, 함수자

operator() 연산자를 중복하고 있는 클래스의 객체를 함수 객체( function object) 혹은 함수자(functor)라 합니다.

그래서 함수자는 함수처럼 사용할 수 있습니다.

함수자는 02. C++, STL QS2의 함수 포인터와 함수자를 참고하세요.

 

아래는 간단한 세가지 함수류(함수, 함수 포인터, 함수자)를 사용한 예제 코드입니다.

 

1 함수, 2 함수 포인터, 3 함수자를 사용한  두 정수의 합 예제입니다.

#include <iostream>
using namespace std;

int Plus(int a, int b)
{
    return a+b;
}
struct CPlus
{
    int operator( )(int a, int b)
    {
        return a+b;
    }
};
void main( )
{
    int result;
    int (*pPlus)(intint) = Plus;
    CPlus oPlus;

    result = Plus( 10 , 20)// 1. 함수 호출
    cout << result << endl;
   
    result = pPlus( 10, 20)// 2. 함수 포인터로 호출
    cout << result << endl;

    result = oPlus( 10, 20)// 3. 함수자로 호출
    cout << result << endl;
}
  1. 30
    30
    30

 위 예제를 보면 함수, 함수 포인터, 함수자의 인터페이스는 모두 같다는 것을 알 수 있습니다.

 

함수자를 사용하면 아래와 같은 장점이 있습니다.

  • 컴파일 시에 함수자를 알고리즘의 인자로 전달할 수 있다.
  • 함수 호출을 인라인(inline)화해서 효율성을 향상시킬 수 있다.
  • 함수자의 정보를 캡슐화하여 지역화(locally)할 수 있다.

 

 2, STL 함수자

STL 함수자는 크게 두 가지로 나눌 수 있습니다.

  • 1, 일반 함수자 : 특정 기능을 수행하는 함수자.

    • 산술 연산 함수자 : 산술 연산 기능을 수행.
    • 비교 연산 함수자 : 비교 연산 기능을 수행.
    • 논리 연산 함수자 : 논리 연산 기능을 수행.

       

  • 2, 함수 어댑터 : 함수류(함수와, 함수 포인터, 함수자)를 인자로 받아 새로운 함수자를 생성.

    • 바인더(binder) : 이항 함수자를 단항 함수자로 변환하는 함수자.
    • 부정자(negator) : 조건자(predicate)를 반대로 변환하는 함수자.
    • 함수 포인터 어댑터(adaptors for pointersto functions) : 함수 포인터를 라이브러리가 필요로 하는 함수자로 변환하는 함수자
    • 멤버 함수 포인터 어댑터(adaptors for pointers to member functions) : 멤버 함수 포인터를 라이브러리가 필요로 하는 함수자로 변환하는 함수자

어댑터의 인자로 사용되는 함수자는 아래 요구 사항을 지켜야 합니다.

  • 단항 함수자는 반드시 argument_type, result_type이 정의되어 있어야 한다. 각각은 함수의 인자 타입과 리턴 타입이다.
  • 이항 함수자는 반드시 first_argument_type, second_argument_type, result_type이 정의되어 있어야 한다. 각각은 함수의 첫번째 인자 타입과 두 번째 인자 타입, 리턴 타입이다.

어댑터들은 인자로 받는 함수자의 위 정의 타입을 이용하여 컴파일 시간에 타입들을 검사합니다.

이러한 타입 정의를 쉽게하기 위해 STL은 기본 클래스 unary_function과 binary_function을 제공한다. 그래서 위 타입들을 직접 작성할 필요는 없습니다. 어댑터는 무조건 위 기본 클래스를 상속받아 만들면 됩니다. ㅡㅡ;

 

또 함수류(함수, 함수 포인터, 함수자) 중에서 bool형을 리턴하는(비교에 사용되는) 함수자를 predicate(조건자, 술어 구문)라 합니다.

 

2.1, 일반 함수자

일반 함수자에는 산술 연산, 비교 연산, 논리 연산 등을 수행하는 함수자들이 있습니다.

 

2.1.1, 산술 연산 함수자

 산술 연산자에는 plus, minus, multiplies, divides, modulus, negate가 있습니다. 모두 템플릿입니다.

 

plus의 간단한 예제입니다.

#include <iostream>
#include <functional>
using namespace std;

void main( )
{
    int result;

    plus<int> oPlus;
    result = oPlus( 10, 20); //oPlus 객체 생성 후 호출
    cout << result << endl;

    result = plus<int>()(10, 20); //임시 객체 생성 후 호출
    cout << result << endl;

}
  1. 30
    30

두 인자를 받아 합을 리턴하는 함수자입니다.

 

그렇다면 이런 함수자를 어디에 써먹나? ㅡㅡ;;

STL은 함수자없이는 동작할 수 없을 정도로 많은 곳에 함수자가 사용됩니다. 

plus 함수자를 사용하는 실제 예제를 보도록 하겠습니다.

 

plus 함수자를 사용한 trasform 알고리즘입니다.

 #include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
    vector<int> v1;
    vector<int> v2;


    v1.push_back(10); 

    v1.push_back(20);
    v1.push_back(30);

    v2.push_back(1);
    v2.push_back(2);
    v2.push_back(3);

    for(int i = 0 ; i < v1.size() ; i++)
        cout << v1[i] << " ";
    cout << endl;

    for(int i = 0 ; i < v2.size() ; i++)
        cout << v2[i] << " ";
    cout << endl;

    transform(v1.begin(), v1.end(), v2.begin(), v1.begin(), plus<int>());

    for(int i = 0 ; i < v1.size() ; i++)
        cout << v1[i] << " ";
    cout << endl;
}

  1. 10 20 30
    1 2 3
    11 22 33

 v1.begin()~v1.end()까지의 원소와 v2의 원소를 함수자(plus<int>())로 연산하여 리턴 결과를 다시 v1.begin()부터 순서대로 저장하는 예제입니다.

그림으로 보면 아래와 같습니다.

plus.png 

 

 

STL plus를 사용하지 않고 사용자 Plus 함수자를 만들어 보도록 하겠습니다.

직접 작성한 Plus 함수자 예제입니다.

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;


struct Plus
{
    int operator( )(int a, int b) const
    {
        return a+b;
    }
};
void main( )
{
    vector<int> v1;
    vector<int> v2;


    v1.push_back(10);

    v1.push_back(20);
    v1.push_back(30);

    v2.push_back(1);
    v2.push_back(2);
    v2.push_back(3);

    for(int i = 0 ; i < v1.size() ; i++)
        cout << v1[i] << " ";
    cout << endl;

    for(int i = 0 ; i < v2.size() ; i++)
        cout << v2[i] << " ";
    cout << endl;

    transform(v1.begin(), v1.end(), v2.begin(), v1.begin(), Plus() );

    for(int i = 0 ; i < v1.size() ; i++)
        cout << v1[i] << " ";
    cout << endl;
}

  1. 10 20 30
    1 2 3
    11 22 33

 결과는 같습니다. 직접 작성한 Plus 함수자를 사용했습니다.

 

STL의 plus처럼 어떤 타입의 합도 계산할 수 있도록 일반화 시키려면 템플릿 클래스로 만들면 됩니다.

다음은 Plus를 템플릿 클래스로 바꾼 예제입니다.

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;

template<typename T>
struct Plus
{
    T operator( )(const T& a, const T& b) const
    {
        return a+b;
    }
};
void main( )
{
    vector<int> v1;
    vector<int> v2;


    v1.push_back(10);

    v1.push_back(20);
    v1.push_back(30);

    v2.push_back(1);
    v2.push_back(2);
    v2.push_back(3);

    for(int i = 0 ; i < v1.size() ; i++)
        cout << v1[i] << " ";
    cout << endl;

    for(int i = 0 ; i < v2.size() ; i++)
        cout << v2[i] << " ";
    cout << endl;

    transform(v1.begin(), v1.end(), v2.begin(), v1.begin(), Plus<int>() );

    for(int i = 0 ; i < v1.size() ; i++)
        cout << v1[i] << " ";
    cout << endl;
}

  1. 10 20 30
    1 2 3
    11 22 33

단순히 템플릿 클래스로 변환한 것 외에 다른 것은 없습니다.

 

마지막으로 2, STL 함수자에서 언급했던 것처럼 만약 위에서 작성한 Plus<> 함수자 클래스를 어댑터 적용이 가능하도록 하려면 이항 함수자이므로 first_argument_type, second_argument_type, result_type을 정의해야 합니다. 가장 간단하게 정의할 수 있는 방법은 binary_function 기본 클래스를 상속받는 것입니다. 위 정의 타입이 필요한 이유는 어댑터에서 컴파일 시간에 타입 검사를 할때 위 정의 타입이 필요하기 때문입니다.

어댑터에 자세한 내용은 아래쪽에서 공부합니다.

 

다음은 binary_function을 상속받은 Plus<> 함수자 클래스입니다.

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;

template<typename T>
struct Plus : public binary_function<T, T, T>
{
    T operator( )(const T& a, const T& b) const
    {
        return a+b;
    }
};
void main( )
{
    vector<int> v1;
    vector<int> v2;

    v1.push_back(10);
    v1.push_back(20);
    v1.push_back(30);

    v2.push_back(1);
    v2.push_back(2);
    v2.push_back(3);

    for(int i = 0 ; i < v1.size() ; i++)
        cout << v1[i] << " ";
    cout << endl;

    for(int i = 0 ; i < v2.size() ; i++)
        cout << v2[i] << " ";
    cout << endl;

    transform(v1.begin(), v1.end(), v2.begin(), v1.begin(), Plus<int>() );

    for(int i = 0 ; i < v1.size() ; i++)
        cout << v1[i] << " ";
    cout << endl;
}
  1. 10 20 30
    1 2 3
    11 22 33

지금 예제에는 어댑터를 사용하지 않기 때문에 굳이 필요하지는 않지만 다음을 위해서 어댑터 적용이 가능하도록 미리 binary_function을 상속받았습니다.

 

간단한 예제를 보이도록 하겠습니다.(binder1st<> 어댑터 사용)

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;

void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;

    transform(v.begin(), v.end(), v.begin(), binder1st< plus<int> > (plus<int>(), 100) );

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;
}
  1. 10 20 30
    110 120 130

v.begin()~v.end()까지의 모든 원소를 네 번째 함수자의 인자로 전달하고 리턴한 결과를 다시 v.begin()부터 순서대로 저장합니다.

 binder1st<>는 이항 연산자를 단항 연산자로 바꾸는 바인더 어댑터입니다. 위 예제의 transform이 단항 연산자를 필요로 하기때문에 이항 연산이 가능한 plus 함수자를 오른쪽 항을 100으로 고정시키고 왼쪽항만 인자로 받을 수 있는 단항 연산자 함수자로 변환합니다.

어댑터는 아래쪽에서 다시 자세히 공부합니다. 지금은 위와 같은 어댑터에 사용하는 plus 함수자는 binary_function 기본 클래스를 상속받아야 한다는 것에 집중하시면 됩니다.

 

우리가 만든 Plus<>가 binary_function을 상속받지 않으면 무시무시한(?) 에러가 발생합니다.

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;

template<typename T>
struct Plus
{
    T operator( )(const T& a, const T& b) const
    {
        return a+b;
    }
};
void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;

    transform(v.begin(), v.end(), v.begin(), binder1st< Plus<int> > (Plus<int>(), 100) );

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;
}
  1. error C2039: 'second_argument_type' : 'Plus<T>'의 멤버가 아닙니다.
    1>        with
    1>        [
    1>            T=int
    1>        ]
    1>        c:\...\main.cpp(28) : 컴파일 중인 클래스 템플릿 인스턴스화 'std::binder1st<_Fn2>'에 대한 참조를 확인하십시오.
    1>        with
    1>        [
    1>            _Fn2=Plus<int>
    1>        ]
  2. ....

무서버~!

 

binary_function 기본 클래스를 상속받으면 쉽게 해결됩니다.

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;

template<typename T>
struct Plus : public binary_function<T, T, T>
{
    T operator( )(const T& a, const T& b) const
    {
        return a+b;
    }
};
void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;

    transform(v.begin(), v.end(), v.begin(), binder1st< Plus<int> > (Plus<int>(), 100) );

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;
}
  1. 10 20 30
    110 120 130

 간단합니다.

 

우리가 직접 first_argument_type, second_argument_type, result_type을 정의해도 해결됩니다.

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
using namespace std;

template<typename T>
struct Plus
{
    typedef T first_argument_type;
    typedef T second_argument_type;

    typedef T result_type;

    T operator( )(const T& a, const T& b) const
    {
        return a+b;
    }
};
void main( )
{
    vector<int> v;

    v.push_back(10);
    v.push_back(20);
    v.push_back(30);

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;

    transform(v.begin(), v.end(), v.begin(), binder1st< Plus<int> > (Plus<int>(), 100) );

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;
}

  1. 10 20 30
    110 120 130

상속이라는 더 깔끔한 방법이 있으니 binary_function을 상속받는 것이 더 좋겠죠?

 

minus, multiplies, divides, modulus, negate함수자는 plus와 모두 비슷하므로 생략합니다.

 

2.1.2, 비교 연산 함수자

 비교 연산자는 equal_to, not_equal_to, greater, less, greater_equal, less_equal이 있습니다. 모두 템플릿 클래스입니다.

 비교 연산 함수자는 모두 predicate입니다. predicate(조건자)는 비교를 수행하는(bool형을 리턴하는 ) 함수류(함수, 함수 포인터, 함수자)을 말합니다.

 

greater의 간단한 예제입니다.

#include <iostream>
#include <functional>
using namespace std;

void main( )
{
    int result;

    greater<int> oGreater;
    result = oGreater( 10, 20); //객체 생성 후 호출
    cout << result << endl;

    result = greater<int>()(10, 20); //임시 객체 생성 후 호출
    cout << result << endl;

    if( greater<int>()(50, 20) )
        cout << "true" << endl;
}
  1. 0
    0
    true

 greater는 두 인자를 받아 첫 번째 인자가 크면 true를 아니면 false를 리턴합니다.

 

 실제 사용 예제입니다.(sort 알고리즘)

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

void main( )
{
    vector<int> v;

    v.push_back(50);
    v.push_back(20);
    v.push_back(40);
    v.push_back(30);
    v.push_back(10);

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;


    sort(v.begin(), v.end()); 
    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;

    sort(v.begin(), v.end(), greater<int>() );

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;
}

  1. 50 20 40 30 10
    10 20 30 40 50
    50 40 30 20 10

 sort는 less를 사용하여 오름차순 정렬을 수행하지만 세 번째 인자로 greater를 전달하면 내림차순 정렬을 합니다.

 

 다음은 직접 작성한 Greater<>입니다.( 어댑터 적용이 가능하도록 binary_function을 상속 받았습니다.)

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

template<typename T>
struct Greater : public binary_function<T, T, bool>
{
    bool operator()(const T& a, const T& b) const
    {
        return a > b;
    }
};
void main( )
{
    vector<int> v;

    v.push_back(50);
    v.push_back(20);
    v.push_back(40);
    v.push_back(30);
    v.push_back(10);

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;


    sort(v.begin(), v.end()); 
    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;

    sort(v.begin(), v.end(), Greater<int>() );

    for(int i = 0 ; i < v.size() ; i++)
        cout << v[i] << " ";
    cout << endl;
}

  1. 50 20 40 30 10
    10 20 30 40 50
    50 40 30 20 10

결과는 같습니다.

 

나머지 비교 연산 equal_to, not_equal_to, less, greater_equal, less_equal은 greater와 비슷하므로 생략합니다.

 

2.1.3, 논리 연산 함수자

 논리 연산은  logical_and, logical_or, logical_not가 있습니다.

 

logical_and의 간단한 예제입니다.(논리 AND 연산을 수행하는 함수자)

#include <iostream>
#include <functional>
using namespace std;

void main( )
{
    int result;

    logical_and<int> and;
    result = and( 10 == 10, 1 < 2); //객체 생성 후 호출
    cout << result << endl;

    result = logical_and<int>()(10 > 20, 'A' < 'B'); //임시 객체 생성 후 호출
    cout << result << endl;

    if( logical_and<int>()(1, 1) )
        cout << "true" << endl;
}
  1. 1
    0
    true

 두 인자를 입력받아 두 인자 모두 true이면 true를 리턴하고 아니면 false를 리턴합니다. AND 연산!

 

간단한 사용 예제입니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

void main( )
{
    vector<bool> v1;
    vector<bool> v2;
    vector<bool> v3(3);


    v1.push_back(true);

    v1.push_back(false);

    v1.push_back(true);


    v2.push_back(false);
    v2.push_back(false);
    v2.push_back(true);

    transform(v1.begin(), v1.end(), v2.begin(), v3.begin(), logical_and<bool>() );

    for(int i = 0 ; i < v3.size() ; i++)
    {
        if( v3[i] )
            cout << "true  ";
        else
            cout << "false  ";
    }
    cout << endl;
}

  1. false  false  true

 간단하죠?

 

너머지는 모두 비슷하므로 생략합니다.

 

 오늘은 여기까지입니다. ^^

...

 

반응형
반응형

#include <iostream>
#include <list>
#include <functional>
#include <algorithm>

using namespace std;


class fuctor{

public :
 int m_MemData;
 static int m_data;

public :
 fuctor(int ini) {
  m_MemData = ini;
 }
 fuctor() {
  m_MemData = m_data++;
 }
 int operator+( int data ) const{
  return this->m_MemData + data;
 }
 void show(int data){
  cout<<m_MemData + data<<"\t";
 }


};

int fuctor::m_data = 1;


class IntSequence{

public :
 fuctor operator()(){
  return fuctor();
 }

};

template<typename T1,typename T2>
class plus1 : public binary_function<T1,T2,T1>{
public :
 T1 operator()(const T1 lhs,const T2 rhs) const {
  return  T1(lhs + rhs);
 }
};

 

int main(){

 list<fuctor> coll2;
 
 generate_n( back_inserter(coll2),10,  IntSequence() );

 transform(coll2.begin(),coll2.end(),coll2.begin(), bind2nd( plus1<fuctor,int>(),10 ) );

 for_each( coll2.begin(),coll2.end(), bind2nd(mem_fun_ref(&fuctor::show) ,100)  );
 
 if(getchar())
  return 0;
}

반응형
반응형

emplate<typename OP1,typename OP2>
class compose_f_gx_t : public unary_function< typename OP2::argument_type,typename OP1::result_type>
{

private :
 OP1 op1; // op(op2(x)) 를 처리한다
 OP2 op2;
public :
 compose_f_gx_t(const OP1& o1, const OP2& o2) : op1(o1), op2(o2)
 {

 }
 typename OP2::result_type
  operator()(const typename OP2::argument_type& x) const {
   return op1(op2(x));
 }

};

template<typename OP1,typename OP2>
inline compose_f_gx_t<OP1,OP2> compose_f_gx(const OP1& o1, const OP2& o2){
 return compose_f_gx_t<OP1,OP2>(o1,o2);
}

반응형
반응형

STL List 에서 기본 자료형이 아닌 클래스 또는 구조체 정렬 하기.

class CObject
{
public:
        int        GetCurX(void);
        int        GetCurY(void) const;
};

list g_stlObjectList;

이런 식으로 직접 만든 클래스를 STL List에 사용하고
클래스 맴버변수에 따른 정렬을 하고 싶다면 operator() 를 사용해서
비교 함수를 재정의 해주시면 됩니다.

struct stLISTsort
{
        bool operator() (const CObject *pObject1, const CObject *pObject2) const
        {
                if ( pObject1->GetCurY() < pObject2->GetCurY() )
                        return true;

                return false;
        }
};

크다 작다 TRUE/FALSE 로 넣어주시면 됩니다.


g_stlObjectList.sort(stLISTsort());

그리고 이와 같이 sort 호출시 재정의된 구조체 또는 클래스를 넣어서
호출하면 위의 연산자로써 정렬을 하게 됩니다.


이 방법 외에도 여러가지 방법이 있습니다.


반응형
반응형

#include <memory>
#include <vector>
#include <list>
#include <Windows.h>
#include <string.h>
#include<iostream>
#include <algorithm>

using namespace std;

 


class node{
private :

 int m_data;

public :
 int getData(){ return m_data; }
 
 node(){
  int templ=30;
  int ddd;
 }
 node(int data){

  m_data=data;
 }
 ~node(){

 }
 bool operator()(node* nodeData){
  if( nodeData->getData() == 30 )
   return true;
  else
   return false;
 }
 void print(){
  cout<<m_data<<"\t";
 }
};

int main(){


 vector<node*> nodes;

 node* pNode=NULL;
 for(int i=0;i<10;++i){
  nodes.push_back(new  node(i*10));
 }

 // mem_fun은 클래스의 멤버 함수를 함수 객체로 만든다.  : 함수객체는 타입이 될 수 있어 조건자로 들어 갈 수 있다
 // mem_fun_ref 는 객체를 넘길때 & 로 넘기는 방식이다
 for_each( nodes.begin(),nodes.end(), mem_fun( &node::print ) );  //http://www.winapi.co.kr/clec/cpp4/38-2-5.htm

 cout<<(*find_if( nodes.begin(),nodes.end(), node() ))->getData()<<endl;

 vector<node*>::iterator it=nodes.begin();
 for(;it!=nodes.end();++it){
  delete (*it);
 }
 nodes.clear();

 return 0;
}

반응형
반응형

C++의 STL 표준 라이브러리는 미리 정의된 여러가지 함수 객체들을 제공한다.

이러한 표준 함수객체들을 사용하기 위해서는 #include <functional> 을 포함해야 한다.

 

이 함수객체들은 알고리즘의 비교,정렬을 위한 기준으로 사용될 수 있다.

less<> 는 알고리즘에서 디폴트 기준으로 사용되는 오름차순 정렬이다.

 

negate<type>()

- 파라미터

plus<type>()

파라미터1 + 파라미터2

minus<type>()

파라미터1 - 파라미터2

multiplies<type>()

파라미터1 * 파라미터2

divides<type>()

파라미터1 / 파라미터2

modulus<type>()

파라미터1 % 파라미터2

equal_to<type>()

파라미터1 == 파라미터2

not_equal_to<type>()

파라미터1 != 파라미터2

less<type>()

파라미터1 < 파라미터2

greater<type>()

파라미터1 > 파라미터2

less_equal<type>()

파라미터1 <= 파라미터2

great_equal<type>()

파라미터1 >= 파라미터2

logical_not<type>()

!파라미터

logical_and<type>()

파라미터1 && 파라미터2

logical_or<type>()

파라미터1 || 파라미터2

 

 

1. 함수어댑터

 

함수어댑터란, 함수객체를 특정값 또는 특정함수와 결합시킨 함수객체이다.

이러한 함수어댑터 역시 <functional> 에 정의되어 있다.

 

표현식

효과

bind1st(op, value)

op(value, 파라미터)

bind2nd(op, value)

op(파라미터, value)

not1(op)

!op(파라미터)

not2(op)

!op(파라미터1, 파라미터2)

 

find_if(coll.begin(), coll.end(), bind2nd(greater<int>(), 42));

이 코드에서 bind2nd(greater<int>(), 42) 는 정수타입 변수가 42보다 큰지를 판단하는 결합된 함수객체를 생성한다.

 

bind2nd는 greater<> 와 같은 이항 함수객체를 단항 함수객체로 변형시킨다.

즉, bind2nd의 두번째 인자를 이항 함수객체의 두번째 인자로 사용하는 것이다.

따라서, greater<>는 42를 두번째 인자로 하여 호출한다.

 

함수어댑터는 스스로가 함수객체이다. 따라서, 함수어댑터를 함수객체와 결합하여 사용할 수 있다.

pos=find_if(coll.begin(), coll.end(), not1(bind2nd(modulus<int>(), 2)));

 

이 코드에서 bind2nd(modulus<int>(), 2) 는 모든 홀수에 대해 1(참) 을 반환한다.

따라서, 이 표현식은 홀수값을 갖는 첫번째 원소를찾는 기준이된다.

하지만 not1() 은 이 결과를 반전시키므로, 짝수값을 갖는 첫번째 원소의 위치를 반환한다.

 

 

2. 멤버함수를 위한 함수어댑터

 

이것은 STL 컨테이너의 각각의 원소들에 대해서 멤버함수를 호출시켜주는 함수 어댑터이다.

표현식효과
mem_fun_ref(op)객체에 대해서 멤버함수 op() 를 호출
mem_fun(op)객체의 포인터에 대해서 멤버함수 op()를 호출

 

#include <iostream>
#include <vector>
#include <functional>
using namespace std;


class person{
 private:
  string name;
 public:
  person(char *nm):name(nm){}
  void print()const{
   cout<<name<<endl;
  }
  void printWithPrefix(string prefix)const{
   cout<<prefix<<name<<endl;
  }
};

int main(){
 vector<person> coll;
 coll.push_back(person("AAAA"));
 coll.push_back(person("BBBB"));
 coll.push_back(person("CCCC"));
 coll.push_back(person("DDDD"));

 

 //각각의 원소에 대해서 멤버함수 print()를 호출
 for_each(coll.begin(), coll.end(), mem_fun_ref(&person::print));
 
 //각각의 원소에 대해서 멤버함수 printWithPrefix()를 호출
 //"person : " 은 멤버함수 printWithPrefix() 의 인자로 전달
 for_each(coll.begin(), coll.end(), bind2nd(mem_fun_ref(&person::printWithPrefix), "person : "));
}

 

3. 일반 함수들을 위한 함수어댑터

 

표현식

효과

ptr_fun(op)

*op(파라미터)

*op(파라미터1, 파라미터2)

 

ptr_fun() 은 기존의 일반 함수들을 함수객체와 같은 형태로 사용하도록 하는 함수객체 어댑터이다.

 

예를들어, 각각의 인자에 대해 무언가를 검사하는 일반 전역함수를 가지고 있을때

bool check(int elem);

 

이러한 경우 조건을 만족하는 첫번째 원소를 찾으려면 다음과 같이 호출한다.

pos=find_if(coll.begin(), coll.end(), ptr_fun(check));

 

조건을 만족하지않는 첫번째 원소를 찾으려면 다음과 같이 사용할 수 있다.

pos=find_if(coll.begin(), coll.end(), not1(ptr_fun(check)));

 

사용자가 두개의 인자를 사용하는 전역함수를 가지고 있다고 가정한다면

pos=find_if(coll.begin(), coll.end(), not1(bind2nd(ptr_fun(strcmp), "AAA")));

 

strcmp는 두개의 string이 같을경우 false를 반환한다.

따라서, 이 구문은 "AAA" 와 같은 첫번째 string의 위치를 반환한다.

 

반응형
반응형


Introduction

클래스 템플릿이 다른 클래스 템플릿을 상속 할 경우, 무엇을 주의 하라는지 설명 하고 있다. 클래스 템플릿을 상속 하고 잘못 사용 했을 경우, "가상 함수"가 제 역활을 못할 수 있거나, 컴파일 자체가 되지 않을 수 있으므로, 이 부분은 꼭 봐야 할 것이다.

Content

시작하기에 앞서, 한가지 사실을 미리 알아두어야 한다. 바로 기반이 되는 클래스 템플릿이 파생 되는 클래스 템플릿에 기입 된 파라미터에 종속 될 경우와 종속되지 않을 경우, 이름을 찾는 규칙이 변화 한다는 사실이다.

이 두 경우 중 종속되지 않을 경우(기반 클래스 템플릿이 파생 클래스 템플릿에 기입된 템플릿 파라미터에)가 더 적은 규칙을 가지고 있으므로, 이를 먼저 정리하겠다.

1. 종속되지 않은 기반 클래스일 경우 주의 해야 할 점

종속되지 않은 기반 클래스일 경우, 일반 클래스를 상속 할 때와 거의 똑같다. 다른 점이 있다면, 한정된 이름이 파생 클래스 템플릿에 기입된 템플릿 파라미터 식별자(이름)와 종속되지 않은 기반 클래스에 있는 이름에 같이 있을 경우, 제일 먼저 종속 되지 않는 기반 클래스에 있는 이름을 먼저 찾으므로, 뜻하지 않게 컴파일이 안 될 수 있다.

template <typename X>
class Base
{
public:
int basefield;
typedef int T; // 이 부분이 중요.
};
template <typename T>
class D2 : public Base<double> // 종속되지 않은 기반 클래스
{
public:
void f()
{
basefield = 7;
}
T strage;
};
void g( D2<int*>& d2, int *p )
{
d2.strage = p; // 이 녀석이 컴파일 되지 않음
}
int main( void )
{
}

여기서 봐야 할 것은 d2.strage이 컴파일 되지 않는 것 이다. 왜냐하면 d2.strage는 int 형이기 때문이다. 아무리 템플릿 파라미터로 int* 를 기입하였어도 int로 기입되었다고 한다. 그 이유가 바로, 식별자 "T"를 평가 할 때, 템플릿 파라미터로 평가 된게 아니라 기반 클래스의 "typedef int T"로 평가 되었기 때문이다.

이는 종속되지 않은 기반 클래스에서 먼저 이름을 찾기 때문인 것을 증명한 셈이다. (이름을 중복되지 않게 짓는 습관을 가진 다면, 이러한 문제는 발생 되지 않을 것이다.)

그렇다면
2. 종속된 기반 클래스일 경우 주의 해야 할 점은 무엇인가?
종속되지 않은 기반 클래스일 경우, 그 기반 클래스 부터 이름을 찾는다고 했는데, 여기선 사용하는 식별자가 한정화 되지 않거나, 종속되지 않으면, 종속된 기반 클래스에서 이름 자체를 찾지 않는다. 이것이 다른 점이다.(하지만 MSVC 에선 찾는다. ... 표준하고 다르다..) 그 이유는 이름을 찾을 수 있게 될 경우, 템플릿 특수화로 이름의 상태가 변경(int형 변수에서 열거형 상수로)될 수 있기 때문이라고 한다.



template <typename X>
class Base
{
public:
int basefield;
};
template <typename T>
class DD : Base<T>
{
public:
void f()
{
basefield = 0;
}
};
template <>
class Base<bool>
{
public:
enum
{
basefield = 42, // basefield의 의미가 변했다.
};
};


이렇게 이름을 찾을 수 없게 함으로써 우회 할 순 있으나, 그 방법이 있는데, this를 사용하여 한정 시키는 방법과 한정자(::)를 사용하여 한정 시키는 방법, 종속시키는 방법, using을 사용 하는 방법들이 있다.

일반적으로 this를 사용 하는 형태가 더 좋다. 왜냐하면 한정자를 사용하는 방법은 자칫 가상함수 호출을 억제 할 수 있기 때문이다. 그러므로 가상 함수 호출에선 this를 사용하고, 선언같은 경우는 한정자를 사용 하면 된다.



template <typename T>
class B
{
public:
enum E
{
e1 = 6, e2 = 28, e3 = 496,
};
virtual void zero( E e = e1 );
virtual void one(E&);
};
template <typename T>
class D : public B<T>
{
public:
void f()
{
// this->E e 라고 선언 할수 없다!
// 이 경우가 바로 한정자 를 사용 해야만 할 때이다.
typename D<T>::E e;
// D<T>::zero() 라고 호출하면 가상 함수를 호출 할 수 없다.
// 그러므로 this를 사용 한다.
this->zero();
// e가 종속적인 데이터이므로 one은 자동적으로 종속적인
// 함수가 되므로, 기반 클래스 템플레서 찾을 수 있다.
one(e);
}
};

3. using을 사용 할 때 주의 해야 할 점

바로 다중 상속을 할 경우, 동일한 이름이 있을 때, 원하는 것을 정확하게 선택하는 것을 프로그래머의 몫으로 돌리기 때문이다. 인간이 한다는건 언제나 실수를 동반 할 수 있다는 것이기에 주의 해야 한다는 것이다.



template <typename T>
class Base
{
public:
int basefield;
};
template <typename T>
class Base2
{
public:
int basefield;
};
template <typename T>
class DD3 : public Base<T>, Base2<T>
{
public:
// 이렇게 using을 사용하여 가시화 시킨다.
using Base<T>::basefield;
//using Base2<T>::basefield;
void f()
{
basefield = 0;
}
};
int main( void )
{
DD3<int> d;
d.f();
}

Digression

가상함수 호출 때문에 꼭 this를 써야 하는건 분명 기억하고 넘어가야 한다. 이 부분은 g++ 에서 컴파일 할 수 있는 상황을 갖은 사람만이 해당 될려나? 안 배워서 나쁠껀 없지만...

2부 부분이 워낙 문법적인 이야기만 나오기 때문에, 지치는 경향이 있다. 하지만 3부와 4부의 내용을 흡수하기 위해선 꼭 필요한 내용이니 잊어먹어도 좋으니 익숙해 지는것을 포기해서는 안된다. 또 훗날 C++ Template Metaprogramming 을 해야 하므로, 너무 쳐지지 않도록 해야 한다.

이것으로 템플릿 이름 9장은 끝났다. 

반응형

+ Recent posts