반응형

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의 위치를 반환한다.

 

반응형

+ Recent posts