함수 포인터의 핵심 개념
함수 포인터는 C++ 언어의 핵심 기능 중 하나로, 런타임에 함수 호출의 유연성을 제공합니다. 메모리 내 함수의 시작 주소를 저장하는 포인터 변수로, 간접적인 함수 호출이 가능하게 합니다.
기본 원리
- 함수 포인터는 실행 코드의 메모리 주소를 저장
- 함수 시그니처(반환형, 매개변수)와 정확히 일치해야 함
- 간접 호출을 통해 다양한 실행 경로를 동적으로 구성 가능
함수 포인터 vs 포인터 반환 함수
| 구분 | 함수 포인터 | 포인터 반환 함수 |
|---|---|---|
| 기본 형태 | 함수 주소 저장 | 메모리 주소 반환 |
| 선언 예시 | int (*funcPtr)(int) | int* getData() |
실전 활용 기법
기본 사용법
// 함수 타입 정의
using CalcFunc = int (*)(int, int);
// 타깃 함수 정의
int multiply(int a, int b) { return a * b; }
// 포인터 할당 및 호출
CalcFunc operation = multiply;
int result = operation(3, 4); // 12 반환
복합 활용
// 함수 포인터 배열
CalcFunc operations[] = {
[](int a, int b) { return a + b; }, // 람다식도 가능
multiply,
[](int a, int b) { return a - b; }
};
// 다양한 연산 실행
for (auto func : operations) {
std::cout << func(10, 5) << " ";
}
// 출력: 15 50 5
고급 패턴 구현
콜백 메커니즘
// 콜백 함수 타입 정의
using Callback = void (*)(int, void*);
// 이벤트 처리기
void processEvent(Callback callback, void* data) {
// 데이터 처리 로직
callback(42, data);
}
// 실제 콜백 구현
void logHandler(int code, void* context) {
std::cout << "Error code: " << code << ", Context: "
<< static_cast(context) << std::endl;
}
int main() {
char context[] = "System Error";
processEvent(logHandler, context);
return 0;
}
전략 패턴 구현
// 정렬 전략 타입
using SortStrategy = bool (*)(int, int);
// 다양한 정렬 구현
bool customSort(int a, int b) {
return (a % 2) == (b % 2) ? a < b : (a % 2) == 1;
}
void sortArray(int arr[], int size, SortStrategy strategy) {
// 버블 정렬 구현
for (int i = 0; i < size-1; i++) {
for (int j = 0; j < size-i-1; j++) {
if (!strategy(arr[j], arr[j+1])) {
std::swap(arr[j], arr[j+1]);
}
}
}
}
int main() {
int data[] = {3, 8, 1, 6, 2, 9};
sortArray(data, 6, customSort);
return 0;
}
현대 C++ 대안
std::function 활용
#include <functional>
#include <vector>
using Operation = std::function;
class MathEngine {
public:
static int power(int a, int b) {
return std::pow(a, b);
}
};
int main() {
std::vector<Operation> operations = {
[](int x, int y) { return x + y; },
MathEngine::power,
std::plus<>()
};
for (auto op : operations) {
std::cout << op(2, 3) << " ";
}
// 출력: 5 8 5
return 0;
}
실무 팁
- 타입 별칭(using/typedef) 사용으로 가독성 향상
- nullptr 검증을 통한 안전한 호출
- 멤버 함수 포인터 사용 시 클래스 컨텍스트 고려
- 현대 C++에서는 std::function과 람다 우선 적용
주요 오류 사례
// 잘못된 타입 매칭
float (*wrongFunc)(double) = sqrt; // 컴파일 오류
// 잘못된 멤버 함수 호출
class Example {
public:
void show() { std::cout << "Hello"; }
};
Example obj;
void (Example::*ptr)() = &Example::show;
(obj.*ptr)(); // 올바른 호출 방식