728x90

이번 시간에는 지금까지 배운 내용을 종합하여 STL의 ptr_fun(), mem_fun(), mem_fun_ref()를 직접 구현해 보도록 하겠습니다.

이 세 함수는 함수자를 요구하는 알고리즘에 일반적인 함수들을 실행가능하도록 랩핑하는 함수들입니다.

이 페이지는 '함수 포인터와 함수자1,2,3'를 공부하고 보시기 바랍니다.

 

앞쪽에서 함수 포인터는 호출형태에 따라 세 가지로 나뉜다고 했습니다.(앞쪽 참고)

  • 첫째, 정적함수 호출 (예  (*pfunc)( 10 );  )
  • 둘째, 주소로 멤버 함수 호출 (예  (data.*pf)( 10 ); )
  • 셋째, 객체의 주소로 멤버 함수 호출 (예 (data->*pf)( 10 ); )

이 세가지 경우처럼 어댑터 역할을 하는 랩핑함수 ptr_fun(), mem_fun(), mem_fun_ref()도 그래서 세 가지입니다.

 

첫 번째 경우인 ptr_fun()함수를 사용한 예제입니다.

for_each()로 콜백하는 코드를 예제로 사용합니다.

bool Printint n )
{
    cout << "int : " << n << endl;
    return true;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = i+1;

    for_each(v.begin(), v.end(), Print );
    cout << "=================" << endl;
    for_each(v.begin(), v.end(), ptr_fun(Print) );
}
  1. int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10
    =================
    int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10

내용은 어렵지 않지만, for_each()에 ptr_fun()을 사용한 이유가 궁금할 수 있습니다. 위 코드에는 굳이 ptr_fun()이 없어도 되지만 다른 어댑터 함수와 같이 사용하는 경우에(not1()등) 꼭 필요합니다. 간단한 설명은 ptr_fun()에서 사용하는 함수자 클래스의 typedef인 argument_type과 result_type을 사용하기 위해서 입니다. 자세한 내용은 STL에서 공부하고 지금은 ptr_fun()구현 사항에 집중하도록 하겠습니다.

 

이제 위와 같은 ptr_fun()과 비슷한 함수 Ptr_fun()을 만들어 보도록 하겠습니다.

//////////////////// server ///////////////////
// 클래스 : ptr_fun() 함수의 함수자 클래스
// 여러가지 기능을 추가로 제공하기 위한 클래스. typedef argument_type, result_type 등....
class Ptr_fun_class
{
    bool (*pf)(int );
public :
    Ptr_fun_class(bool (*_pf)(int ) ):pf(_pf) { }
    bool operator()(int n )
    {
        return pf(n);
    }
};
// 함수 : 함수자를 반환하는 ptr_fun() 함수
Ptr_fun_class Ptr_funbool (*pf)(int ) )
{
    return Ptr_fun_class(pf);  
}


/////////// client ///////////////////
 bool Printint n )
{
    cout << "int : " << n << endl;
    return true;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = i+1;

    for_each(v.begin(), v.end(), Ptr_fun(Print) );
}

  1. int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10

Ptr_fun_class 클래스는 정적함수의 주소를 저장하는 함수자 클래스입니다.

Print함수는 어댑터 기능을 하는 Ptr_fun()함수에 래핑되어 함수가 반환하는 Ptr_func_class의 함수자 객체를 for_each()에 전달합니다.

for_each()는 그 함수자를 호출합니다.(for_each()는 '함수 포인터와 함수자3'를 참고하세요)

 

다음은 모든 자료형이 가능하도록 템플릿 클래스로 수정한 예제입니다.

 //////////////////// server ///////////////////
// 클래스 : ptr_fun()의 함수자 클래스
// 여러가지 기능을 제공할 수 있습니다. typedef argument_type, result_type 등....
template<typename RType, typename AType>
class Ptr_fun_class
{
    RType (*pf)( AType );
public :
    Ptr_fun_class( RType (*_pf)( AType ) ):pf(_pf) { }
    RType operator()( AType n )
    {
        return pf(n);
    }
};
// 함수 : 함수자를 반환하는 ptr_fun() 함수
template <typename RType, typename AType>  
Ptr_fun_class<RType, AType> Ptr_fun( RType (*pf)(AType) )
{
    return Ptr_fun_class<RType, AType>(pf);
}

 

/////////// client ///////////////////
bool Printint n )
{
    cout << "int : " << n << endl;
    return true;
}
void main()
{
    vector<int> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = i+1;

    for_each(v.begin(), v.end(), Ptr_fun(Print) );
}

  1. int : 1
    int : 2
    int : 3
    int : 4
    int : 5
    int : 6
    int : 7
    int : 8
    int : 9
    int : 10

내용은 같습니다.

 

 

두 번째 경우인 mem_fun()를 사용한 예제입니다.

for_each()로 Point의 맴버함수 Print()를 콜백하는 예제입니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};

void main()
{
    vector<Point*> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = new Point(i+1, i+1);

    //for_each(v.begin(), v.end(), &Point::Print ); 에러~~~
    for_each(v.begin(), v.end(), mem_fun(&Point::Print) );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)

컨테이너 v는 Point의 주소들을 저장하고 있습니다.

for_each()는 Point의 멤버 함수 Print()를 호출할 수 없습니다. 이유는?? 우리가 앞쪽에서 공부했듯이 정적함수와 멤버 함수 호출방식(인터페이스)이 다르기 때문입니다. 그래서 호출방식을 통일 시키기위한 함수자(어댑터 객체)가 필요하고 mem_fun()이 멤버 함수를 호출가능하도록 함수자 객체를 생성하여 반환하는 것입니다.

 

이제 우리도 mem_fun()과 비슷한 함수 Mem_fun()을 만들어 보도록 하겠습니다.

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

//////////////////// server ///////////////////
// 클래스 : mem_fun()의 함수자 클래스
// 여러가지 기능을 제공할 수 있습니다. typedef argument_type, result_type 등....
class Point;
class Mem_fun_class
{
    bool (Point::*pf)();
public :
    Mem_fun_class( bool (Point::*_pf)() ):pf(_pf) {}
    bool operator()( Point* p )
    {      
        return (p->*pf)();
    }
};
// 함수 : 함수자를 반환하는 mem_fun() 함수
Mem_fun_class Mem_funbool (Point::*pf) () )
{
    return Mem_fun_class( pf );
}

/////////// client ///////////////////
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    bool Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
        return true;
    }
};

void main()
{
    vector<Point*> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = new Point(i+1, i+1);

    for_each(v.begin(), v.end(), Mem_fun(&Point::Print) );
    cout << "=============" << endl;
    Mem_fun_class functor(&Point::Print);
    for_each(v.begin(), v.end(), functor );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)
    =============
    (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)

Mem_fun()함수는 Mem_fun_class의 객체(함수자)를 반환합니다. for_each()는 이 함수자를 이용하여 정적함수(일반함수)와 동일한 인터페이스로 Point::Print 멤버 함수를 호출합니다.

 

아래는 모든 자료형이 가능하도록 템플릿으로 변환한 예제입니다.

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

//////////////////// server ///////////////////
// 클래스 : mem_fun()의 함수자 클래스
// 여러가지 기능을 제공할 수 있습니다. typedef argument_type, result_type 등....
class Point;
template <typename RType, typename CType>
class Mem_fun_class
{
    RType (CType::*pf)();
public :
    Mem_fun_class( RType (CType::*_pf)() ):pf(_pf) {}
    RType operator()( CType* p )
    {      
        return (p->*pf)();
    }
};
// 함수 : 함수자를 반환하는 mem_fun() 함수
template <typename RType, typename CType>
Mem_fun_class<RType, CType> Mem_fun( RType (CType::*pf) () )
{
    return Mem_fun_class<RType,CType>( pf );
}

/////////// client ///////////////////
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    bool Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
        return true;
    }
};

void main()
{
    vector<Point*> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = new Point(i+1, i+1);

    for_each(v.begin(), v.end(), Mem_fun(&Point::Print) );
    cout << "=============" << endl;
    Mem_fun_class<bool, Point> functor(&Point::Print);
    for_each(v.begin(), v.end(), functor );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)
    =============
    (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)

 내용은 위 예제와 같습니다.

 

 

마지막으로 세 번째 경우인 mem_fun_ref()를 보도록 하겠습니다.

mem_fun()과 비슷합니다. 차이점은 컨테이너 v가 Point 주소가 아닌 Point 객체를 저장한다는 것입니다. for_each()로 Point의 맴버함수 Print()를 콜백하는 예제입니다.

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

class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};
void main()
{
    vector<Point> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = Point(i+1, i+1);

    //for_each(v.begin(), v.end(), &Point::Print ); 에러~~~~
    for_each(v.begin(), v.end(), mem_fun_ref(&Point::Print) );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)

 컨테이너 v가 Point객체를 저장합니다. 내용은 모두 동일합니다.

 

다음은 mem_fun_ref()과 비슷한 함수 Mem_fun_ref()을 만들어 본 예제입니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
//////////////////// server ///////////////////
// 클래스 : mem_fun()의 함수자 클래스
// 여러가지 기능을 제공할 수 있습니다. typedef argument_type, result_type 등....
class Point;
template <typename RType, typename CType>
class Mem_fun_ref_class
{
    RType (CType::*pf)();
public :
    Mem_fun_ref_class( RType (CType::*_pf)() ):pf(_pf) {}
    RType operator()( CType& d )
    {
        return (d.*pf)();
    }
};
// 함수 : 함수자를 반환하는 mem_fun() 함수
template <typename RType, typename CType>
Mem_fun_ref_class<RType, CType> Mem_fun_ref( RType (CType::*pf) () )
{
    return Mem_fun_ref_class<RType, CType>( pf );
}

/////////// client ///////////////////
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    bool Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
        return true;
    }
};

void main()
{
    vector<Point> v(10);
    int i;

    for(i = 0 ; i < (int)v.size() ; i++ )
        v[i] = Point(i+1, i+1);

    for_each(v.begin(), v.end(), Mem_fun_ref(&Point::Print) );
    cout << "=============" << endl;
    Mem_fun_ref_class<bool, Point> functor(&Point::Print);
    for_each(v.begin(), v.end(), functor );
    // delete 생략
}
  1. (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)
    =============
    (1,1)
    (2,2)
    (3,3)
    (4,4)
    (5,5)
    (6,6)
    (7,7)
    (8,8)
    (9,9)
    (10,10)



728x90

+ Recent posts