함수 포인터는 C++에서 함수의 주소를 저장할 수 있는 포인터로, 이를 통해 함수 호출을 유연하게 관리할 수 있습니다. 이번 포스트에서는 함수 포인터의 개념을 이해하기 위해 예제 코드를 한 줄씩 분석하고, 그 의미를 자세히 설명하겠습니다.
int sum(int x, int y)
{
return x + y;
}
int sum(int x, int y)
함수: 두 정수 x
와 y
를 받아 그 합을 반환하는 함수입니다.void functionPointer()
{
// int sum(int x, int y) 을 가리킬 수 있는 함수 포인터
int (*f0)(int, int) = ∑ // 함수의 주소
int (*f1)(int, int) = sum; // 함수의 이름이지만 주소로 평가 되는 것을 허용
int (*f0)(int, int) = ∑
: 함수 sum
의 주소를 가리키는 함수 포인터 f0
를 선언합니다. &sum
은 sum
함수의 주소를 가져옵니다.int (*f1)(int, int) = sum;
: sum
함수 이름을 사용해 함수 포인터 f1
을 초기화합니다. 함수 이름 자체가 함수의 주소로 평가되므로 &
연산자를 생략할 수 있습니다. cout << (*f0)(1, 2) << endl;
cout << f0(1, 2) << endl; // 역참조 생략 허용
(*f0)(1, 2)
: f0
포인터를 역참조하여 sum
함수를 호출합니다. 1
과 2
를 인수로 전달해 sum
이 반환하는 결과인 3
을 출력합니다.f0(1, 2)
: 함수 포인터를 사용할 때 역참조(*
)를 생략할 수 있으므로, 동일하게 sum(1, 2)
를 호출합니다. int(&f2)(int, int) = sum; // 함수 참조
//int (&f3)(int, int) = ∑ // 안 됨
int(&f2)(int, int) = sum;
: 함수 참조 f2
를 선언하고 sum
함수에 바인딩합니다. 함수 참조는 함수 포인터와 비슷하지만, 함수 이름에 직접 참조자를 사용합니다.int (&f3)(int, int) = ∑ // 안 됨
: 함수 참조는 함수 포인터와 달리 주소 연산자(&
)를 사용하지 않으므로 컴파일 오류가 발생합니다. cout << f2(1, 2) << endl;
cout << (*f2)(1, 2) << endl; // 허용
cout << (*sum)(1, 2) << endl; // 허용, 함수 주소가 필요한 곳에서는 암시적으로 함수 주소로 변경
}
f2(1, 2)
: f2
참조를 통해 sum
함수를 호출합니다.(*f2)(1, 2)
: 함수 참조도 함수 포인터처럼 역참조하여 호출할 수 있습니다.(*sum)(1, 2)
: sum
함수 자체도 *sum
으로 역참조할 수 있으며, 이때 암시적으로 함수 주소로 처리됩니다.enum struct RequestType
{
Login, Register, Update, Delete
};
enum struct RequestType
: 네 가지 요청 타입을 정의하는 열거형입니다. 각각 Login
, Register
, Update
, Delete
값이 있습니다.bool onLogin(string id, string password)
{
cout << "onLogin" << endl;
cout << id << endl;
cout << password << endl;
return true;
}
bool onRegister(string id, string password)
{
cout << "onRegister" << endl;
cout << id << endl;
cout << password << endl;
return true;
}
bool onUpdate(string id, string password)
{
cout << "onUpdate" << endl;
cout << id << endl;
cout << password << endl;
return true;
}
bool onDelete(string id, string password)
{
cout << "onDelete" << endl;
cout << id << endl;
cout << password << endl;
return true;
}
onLogin
, onRegister
, onUpdate
, onDelete
함수들: 각각 로그인, 회원가입, 업데이트, 삭제 요청을 처리하는 콜백 함수입니다. 이 함수들은 ID와 패스워드를 출력하고 true
를 반환합니다.void callback()
{
bool (*callbacks[])(string, string) {
onLogin, onRegister, onUpdate, onDelete
};
bool (*callbacks[])(string, string)
: 함수 포인터 배열 callbacks
를 선언하여, onLogin
, onRegister
, onUpdate
, onDelete
함수들의 주소를 저장합니다. callbacks[(int)RequestType::Login]("daoid", "1234");
callbacks[(int)RequestType::Register]("daoid", "1234");
callbacks[(int)RequestType::Update]("daoid", "1234");
callbacks[(int)RequestType::Delete]("daoid", "1234");
}
callbacks[(int)RequestType::Login]("daoid", "1234");
: RequestType::Login
의 인덱스로 callbacks
배열에서 onLogin
함수 포인터를 선택하고, "daoid"
와 "1234"
를 인수로 호출합니다. Register
, Update
, Delete
에 대해서도 같은 방식으로 호출합니다.struct Character
{
int health;
void (*dieCallback)();
};
struct Character
: 캐릭터의 health
와 캐릭터가 죽을 때 호출될 함수 포인터 dieCallback
을 가지는 구조체입니다.void damaged(Character& character)
{
character.health -= 100;
if (character.health <= 0)
{
cout << "died" << endl;
if (character.dieCallback)
character.dieCallback();
}
}
void damaged(Character& character)
: 캐릭터의 health
를 100만큼 감소시키고, health
가 0
이하가 되면 "died"를 출력하고 dieCallback
을 호출합니다.void gameOver()
{
cout << "gameOver" << endl;
}
void playCharacter()
{
Character character0{ 200, nullptr };
Character character1{ 200, gameOver };
damaged(character0);
damaged(character0);
damaged(character1);
damaged(character1);
}
void gameOver()
: 게임 오버 시 호출되는 함수입니다. "gameOver"를 출력합니다.playCharacter()
: Character
인스턴스를 생성하고, character0
은 dieCallback
이 nullptr
인 상태로, character1
은 gameOver
함수를 콜백으로 설정하여 damaged
함수로 호출합니다.character0
: 두 번의 데미지로 인해 죽지만 콜백이 설정되지 않아 아무 일도 일어나지 않습니다.character1
: 두 번의 데미지 후 "died"와 "gameOver"가 출력됩니다.std::function
사용 예제void func(int num)
{
cout << num << endl;
}
typedef float real32_0; // float의 별칭 real32
typedef double real64_0; // double의 별칭 real64
typedef void (*FuncType_0)(int);
using real32_1 = float; // float의 별칭 real32
using real64_1 = double; // double의 별칭 real64
using FuncType_1 = void (*)(int);
typedef
및 using
구문: real32_0
과 real64_0
는 float
과 double
에 대한 별칭입니다.FuncType_0
은 void (*)(int)
타입의 함수 포인터에 대한 별칭입니다.using
키워드를 사용한 real32_1
, real64_1
, FuncType_1
은 각각 같은 역할을 하지만, 최신 C++ 스타일입니다.void type()
{
// 길거나 복잡한 타입에 대해 간략하게 표현할 수 있다.
auto f0 = func;
f0(1);
auto f0 = func;
: auto
키워드를 사용해 함수 포인터 타입을 자동으로 추론하여 f0
를 선언합니다. f0(1)
은 func(1)
을 호출합니다.
auto& f1 = func;
f1(2);
auto& f1 = func;
: 함수 참조 f1
을 선언합니다. f1(2)
은 func(2)
을 호출합니다. function<void(int)> f2 = func;
f2(3);
std::function<void(int)> f2 = func;
: std::function
을 사용하여 func
을 포장합니다. f2(3)
은 func(3)
을 호출합니다. FuncType_0 f3 = func;
f3(4);
FuncType_1 f4 = func;
f4(5);
}
FuncType_0 f3 = func;
: 별칭 FuncType_0
을 사용해 함수 포인터를 선언합니다. f3(4)
은 func(4)
을 호출합니다.FuncType_1 f4 = func;
: using
으로 선언된 FuncType_1
을 사용하여 함수 포인터를 선언합니다. f4(5)
은 func(5)
을 호출합니다.std::function
은 함수 포인터보다 유연하며, 다양한 함수 호출을 지원하는 고급 기능입니다.typedef
및 using
)을 사용하면 복잡한 함수 포인터 타입을 간단하게 표현할 수 있습니다.