함수 포인터

Jaemyeong Lee·2024년 8월 9일
0

FastCampusC++

목록 보기
45/78

C++ 함수 포인터에 대한 예제 코드 분석

함수 포인터는 C++에서 함수의 주소를 저장할 수 있는 포인터로, 이를 통해 함수 호출을 유연하게 관리할 수 있습니다. 이번 포스트에서는 함수 포인터의 개념을 이해하기 위해 예제 코드를 한 줄씩 분석하고, 그 의미를 자세히 설명하겠습니다.

1. 기본적인 함수 포인터 사용 예제

int sum(int x, int y)
{
    return x + y;
}
  • int sum(int x, int y) 함수: 두 정수 xy를 받아 그 합을 반환하는 함수입니다.
void functionPointer()
{
    // int sum(int x, int y) 을 가리킬 수 있는 함수 포인터
    int (*f0)(int, int) = ∑ // 함수의 주소
    int (*f1)(int, int) = sum;  // 함수의 이름이지만 주소로 평가 되는 것을 허용
  • int (*f0)(int, int) = ∑: 함수 sum의 주소를 가리키는 함수 포인터 f0를 선언합니다. &sumsum 함수의 주소를 가져옵니다.
  • int (*f1)(int, int) = sum;: sum 함수 이름을 사용해 함수 포인터 f1을 초기화합니다. 함수 이름 자체가 함수의 주소로 평가되므로 & 연산자를 생략할 수 있습니다.
    cout << (*f0)(1, 2) << endl;
    cout << f0(1, 2) << endl;  // 역참조 생략 허용
  • (*f0)(1, 2): f0 포인터를 역참조하여 sum 함수를 호출합니다. 12를 인수로 전달해 sum이 반환하는 결과인 3을 출력합니다.
  • f0(1, 2): 함수 포인터를 사용할 때 역참조(*)를 생략할 수 있으므로, 동일하게 sum(1, 2)를 호출합니다.
    int(&f2)(int, int) = sum; // 함수 참조
    //int (&f3)(int, int) = &sum; // 안 됨
  • int(&f2)(int, int) = sum;: 함수 참조 f2를 선언하고 sum 함수에 바인딩합니다. 함수 참조는 함수 포인터와 비슷하지만, 함수 이름에 직접 참조자를 사용합니다.
  • int (&f3)(int, int) = &sum; // 안 됨: 함수 참조는 함수 포인터와 달리 주소 연산자(&)를 사용하지 않으므로 컴파일 오류가 발생합니다.
    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으로 역참조할 수 있으며, 이때 암시적으로 함수 주소로 처리됩니다.

2. 콜백 함수 예제

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에 대해서도 같은 방식으로 호출합니다.

3. 콜백을 사용한 구조체 예제

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만큼 감소시키고, health0 이하가 되면 "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 인스턴스를 생성하고, character0dieCallbacknullptr인 상태로, character1gameOver 함수를 콜백으로 설정하여 damaged 함수로 호출합니다.
    • character0: 두 번의 데미지로 인해 죽지만 콜백이 설정되지 않아 아무 일도 일어나지 않습니다.
    • character1: 두 번의 데미지 후 "died"와 "gameOver"가 출력됩니다.

4. 함수 포인터 별칭 및 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);
  • typedefusing 구문:
    • real32_0real64_0floatdouble에 대한 별칭입니다.
    • FuncType_0void (*)(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은 함수 포인터보다 유연하며, 다양한 함수 호출을 지원하는 고급 기능입니다.
  • 별칭(typedefusing)을 사용하면 복잡한 함수 포인터 타입을 간단하게 표현할 수 있습니다.
profile
李家네_공부방

0개의 댓글