[C++] 함수 포인터 사용법


Study/C , C++ , MFC  2020. 4. 20. 19:15

안녕하세요. 명월입니다.


이 글은 C++에서 함수 포인터 사용법에 대한 글입니다.


제가 이전에 포인터와 람다식에 대해서 설명한 적이 있습니다.

링크 - [C++] 배열과 포인터(메모리 주소) 그리고 할당(new){stack과 heap에 대해서}

링크 - [C++] 람다식(functional) 사용법과 클로저(closure) 그리고 auto 자료형


사실 람다식 설명할 때 함수 포인터도 같이 설명을 했어야 했는데.. 정리를 못했네요. 따로 함수 포인터를 정리했습니다.


포인터란 예전에 스택과 힙에서 주소 값을 나타내는 int형 값이라고 설명했습니다. 함수 포인터도 함수를 포인터로 가르킨다는 말 같습니다만 실제로는 다릅니다.

프로그램 언어에서 함수는 힙 메모리에 할당되는 것이 아니고 소스 영역으로 등록이 되는 것이므로 포인터로 메모리의 값을 가르킨다라는 것은 틀립니다.


함수 포인터는 C#의 델리게이트와 같은 의미로 대리자의 의미 입니다. 즉, 메모리를 가르키는 것이 아니고 함수 자체를 가르키는 포인터입니다.

#include <stdio.h>  
#include <iostream>
#include <Windows.h>
#include <vector>
#include <crtdbg.h>
// 메모리 릭을 콘솔에 표시하기 위한 함수
#if _DEBUG 
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__) 
#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) 
#endif
using namespace std;
// sum 함수 반환 값은 int형이고, 두개의 int형 파라미터를 가지고 있다.
int sum(int a, int b)
{
  // 결과는 두 개의 파라미터를 더한다.
  return a + b;
}
// subtract 함수 반환 값은 int형이고, 두개의 int형 파라미터를 가지고 있다.
int subtract(int a, int b)
{
  // 결과는 두 개의 파라미터를 뺀다.
  return a - b;
}
// 실행 함수
int main()
{
  // 함수 포인터는 반환형(*)(파라미터)의 형식으로 구성한다.
  // vector에 반환형은 int형, int형 파라미터를 두개 가지고 있는 vector를 선언한다.
  vector<int(*)(int, int)> list;
  // sum 함수는 반환형은 int에 int형의 두개의 파라미터를 가지고 있다.
  list.push_back(sum);
  // subtract 함수는 반환형은 int에 int형의 두개의 파라미터를 가지고 있다.
  list.push_back(subtract);
  // 람다식이 두개의 int 파라미터, 반환형을 int로 한다.
  list.push_back([](int a, int b)->int
    {
      // 첫번째 파라미터 * (10 * 두번째 파라미터)
      return a * (10 * b);
    });
  // vector를 iterator로 반복한다.
  for (auto ptr = list.begin(); ptr < list.end(); ptr++)
  {
    // 첫번째 파라미터는 2 두번째 파라미터는 1를 넣는다.
    cout << "a , b = " << (*ptr)(2, 1) << endl;
  }
  _CrtDumpMemoryLeaks();
  return 0;
}

결과를 보시면 첫번째는 sum함수가 실행되고 두번째는 subtract함수가 실행되고, 세번째는 람다식이 실행된 것입니다.


함수 포인터는 가운데 * 표시가 있는 것으로 변수형으로 나타낼 수 있습니다.

그렇다면 변수로 사용할 때는 변수명을 「int(*)(int,int) var = sum;」 식으로 사용하지 않습니다.

변수명은 * 표시 바로 뒤에 넣고 선언합니다. 즉, 「int(*var)(int, int) = sum;」 이런 식입니다.

#include <stdio.h>  
#include <iostream>
#include <crtdbg.h>
// 메모리 릭을 콘솔에 표시하기 위한 함수
#if _DEBUG 
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__) 
#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) 
#endif
using namespace std;
// 실행 함수
int main()
{
  // 람다식, int형 파라미터 두개와 반환형 int 함수
  // 여기서 변수명은 var입니다.
  int(*var)(int, int) = [](int a, int b) -> int {
    return a + b;
  };
  // 콘솔 출력
  cout << "a + b = " << var(1, 2) << endl;
  _CrtDumpMemoryLeaks();
  return 0;
}

함수 포인터는 javascript에서의 콜백 함수 같이 vistor 패턴을 구현할 수 있습니다.

#include <stdio.h>  
#include <iostream>
#include <crtdbg.h>
// 메모리 릭을 콘솔에 표시하기 위한 함수
#if _DEBUG 
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__) 
#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) 
#endif
using namespace std;
// 예제 함수, 첫번째는 출력을 위한 파라미터, 두번째는 callback 함수
void function(const char* param, void (*cb)(const char*) = nullptr)
{
  // 콘솔 출력
  cout << "call function param - " << param << endl;
  // 콜백 함수가 null이 아니라면
  if (cb != nullptr)
  {
    // 호출
    cb(param);
  }
}
// callback 함수
void callback(const char* param)
{
  // 콘솔 출력
  cout << "call callback param - " << param << endl;
}
// 실행 함수
int main()
{
  // 예제 함수 호출..
  function("hello world");
  // 예제 함수 호출, callback을 넣는다.
  function("good", callback);

  _CrtDumpMemoryLeaks();
  return 0;
}

여기서 함수 포인터는 c++의 function 라이브러리로 좀 더 간단하게 표현할 수 있습니다.

#include <stdio.h>  
#include <iostream>
#include <crtdbg.h>
// 메모리 릭을 콘솔에 표시하기 위한 함수
#if _DEBUG 
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__) 
#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) 
#endif
// 함수 포인터를 간단하게 표현하기 위한 라이브러리
#include <functional>
using namespace std;
// 실행 함수
int main()
{
  // 반환형이 int형인 파라미터 int형이 두개인 람다식 함수
  function<int(int, int)> var = [](int a, int b) -> int
  {
    return a + b;
  };
  // 람다식 호출하면서 콘솔 출력
  cout << "a + b = " << var(1, 2) << endl;

  _CrtDumpMemoryLeaks();
  return 0;
}

사실 이 함수식은 단순히 함수 포인터를 표현하기 좋게 나타내는 것만은 아닙니다.

class의 private기능을 class 선언없이 함수형만으로도 표현할 수 있습니다.

#include <stdio.h>  
#include <iostream>
#include <crtdbg.h>
#include <functional>
// 메모리 릭을 콘솔에 표시하기 위한 함수
#if _DEBUG 
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__) 
#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) 
#endif
using namespace std;
// 함수를 선택하는 함수
// 여기 반환값을 함수 포인터로 나타내면 에러가 발생한다.
function<void()> select(bool i)
{
  // 파라미터가 true의 경우 아래의 함수를 리턴
  if (i)
  {
    return []() -> void {
      cout << "bool true function " << endl;
    };
  }
  // false의 경우는 아래의 경우를 리턴
  return []() -> void {
    cout << "bool false function " << endl;
  };
}
// 실행 함수
int main()
{
  // select 함수에서 true를 넣고 함수를 리턴해서 실행
  select(true)();
  // select 함수에서 false를 넣고 함수를 리턴해서 실행
  select(false)();
  _CrtDumpMemoryLeaks();
  return 0;
}

여기까지 C++에서 함수 포인터 사용법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.