Media Log


1. 함수 포인터(Function Pointer)


오늘은 번외편의 시작으로 함수 포인터(Function Pointer)에 대해 알아보려고 합니다. 포인터(Pointer) 앞에 함수(Function)가 붙으니 대충 어떠한 녀석인지 감이 잡히시나요? 당연히 함수를 가리키는 포인터가 있다는 것은 함수에도 주소가 존재함을 알 수 있습니다. 함수명은 함수의 시작 주소를 의미하고, 이 함수 포인터를 선언할 때에는 함수 시그너쳐(signature)와 같도록 선언해야 합니다. 다른 말로는 원형과 같도록 선언해야 한다고 말할 수 있겠네요.

만약에 아래의 원형을 갖는 함수를 가리키는 포인터를 선언하려면 어떻게 해야 할까요?

int sum(int a, int b)

반환형은 int이며, 매개변수는 int, int임을 알 수 있습니다. 그렇다면, 함수 포인터는 아래와 같이 선언할 수 있습니다.

int (*pf)(int, int) = sum;

위와 같이 선언을 하고 초기화를 한 후로부터, 함수 포인터 pf는 함수 sum의 시작 주소를 가리킵니다. 이제부터 함수 포인터 pf를 가지고 함수 sum에 인자를 전달하는 것과 동일하게 전달할 수 있습니다. 한번 아래의 예제를 봅시다.

#include <iostream>
using namespace std;

int sum(int a, int b)
{
	return a + b;
}

int main()
{
	int (*pf)(int,int) = sum;

	cout << "pf(int,int): " << pf(4, 5) << endl;
	cout << "sum(int,int): " << sum(4, 5) << endl;
	cout << "pf address = " << pf << endl;
	cout << "sum address = " << sum << endl;

	return 0;
}

결과:

pf(int,int): 9
sum(int,int): 9
pf address = 003A1930
sum address = 003A1930
계속하려면 아무 키나 누르십시오 . . .


위의 예제의 결과를 보시면 pf(4, 5)나 sum(4, 5) 둘 다 같은 값을 반환하고, 주소 또한 같다는 것을 확인하실 수 있습니다. 추가로, 만약 함수 sum의 반환형이 void라면 아래와 같이 원형을 가지겠죠?

void sum(int a, int b)

이는 반환형이 void고 매개변수의 타입이 int, int임을 알 수 있습니다. 그렇다면, 함수 포인터는 아래와 같이 선언할 수 있겠습니다.

void (*pf)(int, int) = sum;

의외로 간단하죠? 정적 함수 포인터는 여기까지 설명하도록 하고, 이번에는 멤버 함수 포인터에 대해서 알아보도록 합시다.


2. 멤버 함수 포인터(Member Function Pointer)


여기서 멤버 함수(Member Function)는 클래스 내부에 있는 함수로써, 클래스가 멤버로 가지는 함수를 의미합니다. 그렇다면 멤버 함수 포인터(Member Functon Pointer)는 멤버 함수를 가리키는 포인터를 의미한다는 것을 알 수 있습니다. 멤버 함수 포인터를 선언하는 방법은 정적 함수 포인터를 선언하는 방법과 조금 다릅니다. 만약 Rectangle 클래스에서 area 함수의 원형이 아래와 같다고 해봅시다.

int Rectangle::area(int width, int height)

위 area 함수의 반환형은 int이며, 매개변수의 타입은 int, int입니다. 그리고 area 함수는 Rectangle 클래스의 멤버 함수입니다. 이는 아래와 같이 함수 포인터를 선언할 수 있습니다.

int (Rectangle::*pf)(int,int) = &Rectangle::area;

조금 복잡한가요? 확실한 이해를 위해 예제를 보면서 진행하도록 합시다.

#include <iostream>
using namespace std;

class Rectangle
{
	int width;
	int height;
public:
	explicit Rectangle(int _width = 0, int _height = 0) : width(_width), height(_height) { }
	int area()
	{
		return width * height;
	}
};

int main()
{
	Rectangle rc(10, 5);
	int (Rectangle::*pf)(void) = &Rectangle::area;

	cout << "rc.area(): " << rc.area() << endl;
	cout << "(rc.*pf)(): " << (rc.*pf)() << endl;

	return 0;
}

결과:

rc.area(): 50
(rc.*pf)(): 50
계속하려면 아무 키나 누르십시오 . . .


위 예제에서는 함수 포인터 pf가 Rectangle 클래스에서 area 멤버 함수의 시작 주소를 가리키도록 하고, rc.area()와 (rc.*pf)()의 반환값을 출력합니다. 결과를 보시면, 두 결과값이 동일하다는 것을 알 수 있습니다. (rc.*pf)()처럼 객체를 통해 멤버 함수를 호출할 때에는 .* 연산자를 사용합니다. (연산자 우선순위 때문에 괄호를 제거하여 rc.*pf()와 같이 호출하려고 한다면 오류가 발생합니다. 반드시 괄호를 포함하도록 합시다.) 예를 들어서, Lion 클래스의 cry 함수 원형이 아래와 같다고 가정해봅시다.

void Lion::cry()

이는 반환형이 void이고, 매개변수의 타입도 void입니다. 그리고, cry 함수는 Lion 클래스의 멤버 함수 입니다. 이는 아래와 같이 함수 포인터를 선언할 수 있습니다.

void (Lion::*pf)() = &Lion::cry;

이제 감이 잡히시나요? 정적 함수 포인터를 이해하셨으면, 멤버 함수 포인터도 이해하기가 쉽습니다. 멤버 함수 포인터에 대한 설명은 여기까지 하도록 하고, 오늘은 이쯤에서 강좌를 마무리 지으려고 합니다. 여기까지 읽느라 수고 많으셨습니다.

  1. 잘보고 갑니다 at 2016.04.13 13:23 신고 [edit/del]

    ㅈㄱㄴ

    Reply
  2. Ryan at 2016.09.15 00:09 신고 [edit/del]

    감사합니다. 그런데 메인함수 2행에서 int (Rectangle::*pf)(int, int) = &Rectangle::area; 로 안하신 이유(void로 하신 이유)가 무엇인지요?

    Reply
    • 아기족제비 at 2017.02.04 11:04 신고 [edit/del]

      여기서 우리가 포인터로 가르켜야할 곳은 class가 아니라 class내부에 있는 area() 함수입니다.
      그리고 *pf를 만들때
      Rectangle::*pf로 했으니 이미 class 내부의 함수로 만들겠다고 선언한것 같습니다.
      그리고 실제 코드와 설명 코드와 약간 차이가 있습니다.

      처음 설명할때 Rectangle::area라는 함수는
      (int width, int height) 이렇게 int 변수를 2개를 받지만 실제 코드에서는 그러지 않습니다.

  3. 광영 at 2017.04.28 15:33 신고 [edit/del]

    함수포인터는 어디에서 쓰이나요? 일단 상상하기로는 함수 포인터의 배열을 만든 뒤, 그 배열에 함수 포인터를 왕창 받아서 루프문으로 모든 함수포인터에 값을 넣어줘서 돌리는 시나리오 정도가 떠오르는데

    Reply
  4. ㅇㅇ at 2017.12.14 16:33 신고 [edit/del]

    이거 C#에 있는 델리게이트랑 비슷한가요?

    Reply
  5. 감사합니다 at 2018.02.01 10:56 신고 [edit/del]

    gcc로 컴파일 하고 있는데요.
    gcc는 fp의 값이 1로 출력되고
    sum도 값이 1로 출력되네요 ㅠㅠ

    Reply

submit

티스토리 툴바