728x90

C++에서 함수 포인터는 함수자(functor)를 구현할 때 유용하게 사용되며 STL이나 콜백 메커니즘을 구현할 때도 유용하게 사용될 수 있습니다.

 

1, 함수 포인터의 종류

C++언어의 함수는 세 가지 방식으로 호출할 수 있습니다.

  • 첫째, 정적 함수 호출(전역함수, 클래스 내의 정적함수, 네임 스페이스 내의 전역함수등)
  • 둘째, 객체로 멤버 함수 호출(객체 자체로 호출)
  • 셋째, 객체의 주소로 멤버 함수 호출(객체의 주소로 호출)

위 세 가지에 대한 함수 포인터 선언및 호출 방식이 다릅니다.

 

우리는 이미

첫째, 정적 함수의 주소를 저장하는 함수 포인터는 알고 있습니다.

 

여기서 다시 복습해 볼까요?

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public:
    // ... 여러 멤버들 ..
    static void Print(int n) // 2.Point 클래스 static 함수
    {
        cout << "n = " << n << endl;
    }
};

void Print(int n) // 1.전역 함수
{
    cout << "n = " << n << endl;
}

namespace NetGong
{
    void Print(int n) // 3.NetGong 네임스페이스  내의 전역함수
    {
        cout << "n = " << n << endl;
    }
}

void main( )
{
    void (*pfunc)(int ); // 함수 포인터 선언 - C언어 QuickStart1 참고
   
    pfunc = &Print; // 1.전역함수 주소 저장
    pfunc( 10 ); // 1.전역함수 호출

    pfunc = &Point::Print; // 2.Point 클래스 static 함수 저장
    pfunc( 10 ); // 2.Point 클래스 static 함수 호출

    pfunc = &NetGong::Print; // 3.NetGong 네임스페이스  내의 전역함수 저장
    pfunc( 10 ); // 3.NetGong 네임스페이스  내의 전역함수 호출
}
  1. n = 10
    n = 10
    n = 10

뭐.. 별로 설명할 것이 없습니다. 

알아 둘 것은 위 세 가지 경우 모두 같은 포인터 변수를 사용한다는 것입니다. 호출 방식이 같거든요.

함수의 주소를 저장할 때 Print가 함수 주소이므로 &Print 와 같이 &를 붙여도 그만 붙이지 않아도 그만이지만 명시적으로 &붙여 사용합니다. 연산자의 위치와 용도에 따라 의미가 달라지는 걸 아시죠? 이곳에서 &가 있든 없든 같은 코드입니다. 또, 아래쪽에서 배울 맴버함수는 꼭 & 연산자가 있어야 합니다. 하여튼 &를 붙이는 방식을 선호하므로 꼭 붙여서 사용하는 습관을 들이세요.

 

이제 둘째, 셋째 멤버 함수호출인 멤버 함수 포인터를 사용하는 예제를 공부해 보도록 하겠습니다.

  • 멤버 함수 포인터 선언

 class Point
{

//멤버 ...
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};

 void (Point::*pfunc)( ); //멤버 함수 주소를 저장하기 위한 함수 포인터 선언

 일반 함수 포인터라면 void (*pfunc)( ); 라고 선언했겠지만 Point 클래스의 멤버 함수 포인터이므로 Point::* 와 같은 연산자를 사용합니다.

  • 멤버 함수 포인터를 이용한 호출

    • 객체로 호출 : (객체.*pfunc)( );
    • 객체의 주소로 호출 : (객체주소->*pfunc)( );

.* 연산자와 ->*연산자를 이용하여 각각 객체와 객체의 주소로 함수 포인터가 가리키는 멤버함수를 호출할 수 있습니다. 또 연산자 우선순위 때문에 ( )로 묶어 주어야합니다. 주의하세요.

 

2, 멤버 함수 포인터 실습

그럼 실제 함수 포인터 예제를 보도록 하겠습니다.

#include <iostream>
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( )
{
    Point d1(2,3);
    Point *p = &d1;

    void (Point::*pfunc)( ); //멤버 함수 주소를 저장하기 위한 함수 포인터 선언
    pfunc = &Point::Print; 

    d1.Print(); //둘째, 객체로 멤버 함수 호출
    p->Print();  //셋째, 객체의 주소로 멤버 함수 호출

    (d1.*pfunc)( ); //둘째, 객체로 함수 포인터 이용한 멤버 함수 호출
    (p->*pfunc)( ); //셋째, 객체의 주소로 함수 포인터 이용한 멤버 함수 호출

    Point d2, d3(5,5);
    (d2.*pfunc)();
    (d3.*pfunc)();
}
  1. (2,3)
    (2,3)
    (2,3)
    (2,3)
    (0,0)
    (5,5)

객체는 연산자 .*를 사용하고 객체의 주소는 ->* 연산자를 사용한다는 것만 주의하세요.

 

또 다른 예제를 보겠습니다.

#include <iostream>
using namespace std;
class Point
{
    int x, y;
public:
    Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
    bool Compare(const Point& arg)
    {
        return x==arg.x && y == arg.y ? true : false ;
    }
    void Print( )
    {
        cout << "(" << x << "," << y << ")" << endl;
    }
};

void main( )
{
    Point d1(2,3);
    Point d2(5,5);
    Point *p = &d1;

    bool (Point::*pfunc)(const Point& ); //멤버 함수 주소를 저장하기 위한 함수 포인터 선언
    pfunc = &Point::Compare;

    cout << d1.Compare(d2) << endl; //둘째, 객체로 멤버 함수 호출
    cout << p->Compare(d1) << endl; //셋째, 객체의 주소로 멤버 함수 호출

    //둘째, 객체로 함수 포인터 이용한 멤버 함수 호출
    cout << (d1.*pfunc)(d2) << endl;
    //셋째, 객체의 주소로 함수 포인터 이용한 멤버 함수 호출
    cout << (p->*pfunc)(d1) << endl;
}
  1. 0
    1
    0
    1

출처 : http://blog.daum.net/coolprogramming/76

728x90

+ Recent posts