포스트

[함수 포인터] 01. 함수 포인터와 멤버 함수 포인터

프로그램에서 정의된 함수는 프로그램 실행 시 메모리에 로드됩니다.

이때, 함수의 이름은 실제로 해당 함수의 기계어 코드가 저장된 메모리 주소를 참조하는 식별자로 사용됩니다.

이 주소는 함수 코드의 시작 지점을 나타내며, 이를 통해 함수를 간접적으로 호출할 수 있습니다.

함수 포인터는 이러한 함수의 시작 주소를 저장하는 변수입니다. 즉, 함수 포인터는 특정 함수를 가리키는 포인터로, 해당 함수의 메모리 주소를 담고 있습니다.

이를 활용하여 프로그램 내에서 다양한 함수를 동적으로 호출하거나, 콜백과 같은 고급 프로그래밍 기법을 구현할 수 있습니다.


함수 포인터의 기본 구조

함수 포인터의 기본 형태는 다음과 같습니다.

반환_타입 (*포인터_이름)(매개변수_타입);

이때, 반환 타입매개 변수 타입은 저장할 함수의 시그니처와 일치해야 합니다.

예를 들어, 다음과 같은 함수를 함수 포인터로 만든다면, 반환 타입과 매개 변수 타입을 일치시켜 다음과 같이 만들 수 있습니다.

1
2
3
4
5
6
int add(int a, int b);
int main()
{
	int (*funcPtr)(int, int) = add;
	funcPtr(1, 2);
}

함수 포인터의 초기화

위의 예제 코드에서 잠깐 나왔지만, 함수 포인터에 함수를 넣어 초기화 하는 방법은, 함수 포인터해당 함수의 식별자로 초기화 해줍니다.

여기에서 해당 함수의 식별자란, 함수의 이름을 말합니다.

함수의 이름은 컴파일 시 해당 함수의 기계어 코드가 저장된 메모리 주소를 참조하는 식별자로 변환됩니다.

또한, C++에서는 함수 이름을 함수 포인터에 할당할 때, 함수의 주소를 암묵적으로 변환하여 저장합니다. 따라서 주소 연산자 &를 사용하지 않아도 됩니다.

1
2
3
4
5
funcPtr = add; // 암시적 주소 저장
funcPtr = &add; // 명시적 주소 저장

funcPtr(1, 2); // 함수 사용
(*funcPtr)(1, 2); // 명시적 역참조를 사용하여 add 함수를 호출합니다. 이는 add(1, 2)를 호출하는 것과 동일합니다.

객체(Class)에서의 함수 포인터

C++에서 클래스(객체)와 관련된 함수 포인터를 사용하는 방법은 일반 함수 포인터와는 다소 차이가 있습니다.

클래스의 멤버 함수는 객체의 상태에 접근할 수 있어야 하기 때문에, 멤버 함수 포인터를 사용할 때는 객체의 컨텍스트도 함께 고려해야 합니다.

클래스 멤버 함수 포인터

클래스의 멤버 함수 포인터는 클래스 타입과 함수 시그니처를 모두 포함해야 합니다.

멤버 함수 포인터의 선언은 다음과 같은 형태를 가집니다.

반환_타입 (클래스명::*함수_식별자)(매개변수_타입);

물론, 이렇게 한다고 해서 끝나는게 아닙니다.

이를 호출하기 위해서는 위에서도 말했듯이 클래스의 멤버 함수는 객체에 접근할 수 있어야 하기 때문에 인스턴스를 함께 제공해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

class MyClass
{
public:
	void Add(int a, int b)
	{
		std::cout << "MyClass Add: " << (a + b) << std::endl;
	}
};

int main()
{
	void (MyClass::*FuncPtr)(int, int) = &MyClass::Add;

	MyClass myClass1; // 객체의 인스턴스가 있어야 한다.
	(myClass1.*FuncPtr)(1, 2); // myClass1의 컨텍스트에서 Add에 접근.

	MyClass myClass2;
	(myClass2.*FuncPtr)(1, 2); // myClass2의 컨텍스트에서 Add에 접근.
}

이는, this 포인터와 관련이 있기도 한데, 우선은 우리가 평소에 멤버 함수를 호출하듯이 호출한다고 생각하고 사용하는 것이 이해하기 빠를 것 같습니다. this 포인터와 관련된 내용은 함수 호출 규약의 thiscall과 연관되어 있기에 한 번 일어보는 것도 추천드립니다.

1
2
MyClass myClass3;
myClass3.Add(1, 2); // 일반적으로 멤버 함수를 호출하는 방법

참고

함수 호출 규약 (cdecl, stdcall, thiscall, fastcall)

[함수 포인터] 02. 콜백 함수와 델리게이트

[함수 포인터] 03. 일급 객체와 고차 함수

[함수 포인터] 04. 함수 포인터를 보기 좋게 만들기

이 기사는 저작권자의 CC BY-NC-ND 4.0 라이센스를 따릅니다.