C++에서 클래스나 구조체의 멤버 함수(member function)를 다룰 때는 일반 함수 포인터와 달리 특별한 문법이 필요합니다. 멤버 함수 포인터는 객체 포인터 또는 this 호출을 통해 실행되며, 해당 클래스(또는 그 파생 클래스)의 멤버만 호출할 수 있어 타입 안전성(type safety)이 보장됩니다.
멤버 함수 포인터란?
일반 함수 포인터는 단순히 함수의 주소를 저장하지만, 멤버 함수 포인터는 클래스 범위(class scope)에 속해 있다는 점이 다릅니다. 따라서 선언 시 클래스 이름을 명시해야 하며, 호출할 때도 반드시 객체 또는 객체 포인터가 필요합니다.
- 선언 형식:
반환타입 (클래스명::*포인터명)(매개변수 목록) - 호출 방식: 객체 참조 시
(obj.*fp)(인자), 객체 포인터 사용 시(obj->*fp)(인자)
예제 코드
아래 예제는 AB 클래스의 두 멤버 함수 sub와 div를 함수 포인터를 통해 간접적으로 호출하는 방법을 보여줍니다.
#include <iostream>
using namespace std;
class AB {
public:
int sub(int a, int b) {
return a - b;
}
int div(int a, int b) {
return a / b;
}
};
// 함수 포인터를 매개변수로 받아 멤버 함수 호출
int res1(int m, int n, AB* obj, int(AB::*fp)(int, int)) {
return (obj->*fp)(m, n);
}
// 함수 포인터를 매개변수로 받아 멤버 함수 호출
int res2(int m, int n, AB* obj, int(AB::*fp2)(int, int)) {
return (obj->*fp2)(m, n);
}
int main() {
AB ob;
cout << "Subtraction is = " << res1(8, 5, &ob, &AB::sub) << endl;
cout << "Division is = " << res2(4, 2, &ob, &AB::div) << endl;
return 0;
}실행 결과
Subtraction is = 3 Division is = 2
코드 설명
1. 멤버 함수 포인터 전달
&AB::sub와 같이 클래스 이름과 범위 지정 연산자(::)를 사용하여 멤버 함수의 주소를 얻습니다. 이 주소는 일반 함수 포인터 변수에 저장할 수 없으며, 반드시 int(AB::*)(int, int) 형태의 멤버 함수 포인터 타입이어야 합니다.
2. 간접 호출 연산자
객체 포인터로 멤버 함수를 호출할 때는 ->* 연산자를 사용합니다. 이는 포인터 역참조와 멤버 접근을 동시에 수행하는 특수 연산자입니다. 만약 객체 자체(포인터가 아닌)를 사용한다면 .* 연산자를 대신 사용합니다.
3. 콜백 및 유연한 설계
이러한 기법은 콜백(callback) 구현, 전략 패턴(Strategy Pattern), 이벤트 핸들링 등 런타임에 호출할 멤버 함수를 동적으로 결정해야 하는 상황에서 유용하게 활용됩니다. 다만 현대 C++에서는 가독성과 유연성이 더 좋은 std::function과 람다(lambda) 표현식을 함께 사용하는 경우가 많습니다.