함수 포인터

함수포인터를 정의하는 방식은, 선언, 대입,호출로 이루어진다.

함수 포인터 (function pointer)는 3세대 프로그래밍 언어(PL/I, 코볼, 포트란,[1] dBASE dBL, 그리고 C)와 객체 지향 프로그래밍 언어(C++과 D)[2]에서 지원되는 포인터의 한 종류이다.

데이터 값을 가리키는 대신, 함수 포인터는 메모리 내에서 실행 가능한 코드를 가리킨다. 역참조될 때, 함수 포인터는 가리키는 함수를 보통의 함수 호출처럼 작동시키고 인자를 보내는데 사용될 수 있다. 이러한 작동은 함수가 고정된 이름이나 주소를 사용해서 직접적으로가 아니라 변수를 통해서 간접적으로 호출되기 때문에 또한 "간접" 호출로도 알려져 있다.

함수 포인터들은 런타임 값들에 기반해 함수를 골라서 실행하게 하는 간단한 방식을 제공함으로써 코드를 간소화하는데 사용될 수 있다.

간단한 함수 포인터들 편집

함수(또는 서브루틴) 포인터의 간단한 구현은 실행 가능한 메모리 안에서 변수가 함수의 주소를 포함하고 있는 경우이다. C 같은 경우 일반적으로 이 방식으로 함수 포인터를 구현한다. 이러한 포인터들은 일반적으로 현대 언어들보다는 타입이 안전하지 않다. 현대 언어들은 함수의 반환 값의 자료형과 함수에 대한 매개변수의 자료형 정보처럼, 함수 포인터 변수와 자료형 정보가 더 관련있기 때문이다.[3]

C 예제 편집

다음의 C 프로그램은 함수 포인터의 두가지 사용을 보여준다.

  • func1은 하나의 더블형 매개변수를 가지며 다른 더블형을 반환하고, 센티미터를 인치로 변환하는 역할을 한다.
  • func2는 정수형 그리고 상수 문자 배열에 대한 포인터를 가지며 문자에 대한 포인터를 반환하고, 문자 배열에서 주어진 문자의 첫 번째 발생에 대한 포인터를 반환하는 문자열 라이브러리 함수의 역할을 한다.
    #include <stdio.h>  /* for printf */
    #include <string.h> /* for strchr */
    
    double cm_to_inches(double cm) {
    	return cm / 2.54;
    }
    
    int main(void) {
    	double (*func1)(double) = cm_to_inches;
    	char * (*func2)(const char *, int) = strchr;
    	printf("%f %s", func1(15.0), func2("Wikipedia", 'p'));
    		/* prints "5.905512 pedia" */
            return 0;
    }
    
    다음의 프로그램은 함수 포인터를 두 함수들(sin or cos) 중 하나를 다른 함수(compute_sum, 함수의 리만 적분의 근사치를 계산한다)에서 간접적으로 작동시키기 위해 사용한다. 이 프로그램은 main 함수가 함수 compute_sum를 두 번 호출함으로써 작동하며, 처음 라이브러리 함수 sin에 대한 포인터를 전달하고, 두번째에는 cos 함수에 대한 포인터를 전달한다. 결과적으로 함수 compute_sum는 자신의 함수 포인터 funcp를 여러 번 역참조하고, 작동된 함수 결과들을 더하고 결과인 sum을 반환하면서, 두 함수들 중 하나를 간접적으로 호출한다. 두 sum들은 main에 의해 표준 출력에 써진다.
    #include <math.h>
    #include <stdio.h>
    
    // Function taking a function pointer as an argument
    double compute_sum(double (*funcp)(double), double lo, double hi)
    {
        double  sum = 0.0;
    
        // Add values returned by the pointed-to function '*funcp'
        for (int i = 0;  i <= 100;  i++)
        {
            double  x, y;
    
            // Use the function pointer 'funcp' to invoke the function
            x = i / 100.0 * (hi - lo) + lo;
            y = (*funcp)(x);
            sum += y;
        }
        return sum / 101.0;
    }
    
    int main(void)
    {
        double  (*fp)(double);      // Function pointer
        double  sum;
    
        // Use 'sin()' as the pointed-to function
        fp = sin;
        sum = compute_sum(fp, 0.0, 1.0);
        printf("sum(sin): %f\n", sum);
    
        // Use 'cos()' as the pointed-to function
        fp = cos;
        sum = compute_sum(fp, 0.0, 1.0);
        printf("sum(cos): %f\n", sum);
        return 0;
    }
    

기능자 (Functor) 편집

기능자, 또는 함수 객체들은 함수 포인터와 비슷하며, 비슷한 방식으로 사용될 수 있다. 기능자는 객체가 함수 호출과 같은 구문을 사용하는 표현에서 사용될 수 있게 허용함으로써 함수 호출 연산자를 구현하는 클래스 타입의 객체이다. 기능자들은 간단한 함수 포인터들보다 더 강력한데, 그들 자신의 데이터 값을 포함할 수 있고 프로그래머가 클로저를 에뮬레이트하게 한다. 이것들은 또한 멤버 함수를 콜백 함수로 사용해야될 필요가 있는 경우에 콜백 함수로 사용될 수 있다.[4]

많은 "pure" 객체 지향 언어들은 함수 포인터를 지원하지 않는다. 단일 멤버 함수를 정의하는 인터페이스에 대한 참조를 사용해서 몇몇 비슷한 것이 구현되어있을 수 있다. C#과 비주얼 베이직 닷넷 같은 언어들은 위임으로 안전한 타입의 함수 포인터를 구현한다.

1급 함수(first-class function)를 지원하는 다른 언어에서, 함수들은 데이터로 여겨지며 전달, 반환 그리고 다른 함수에 의해서 직접적으로 동적으로 생성될 수 있어서 함수 포인터에 대한 필요를 없애준다.

함수를 호출하기 위한 함수 포인터의 확장적인 사용은 현대 프로세서에서 코드의 느려짐을 유발할 수 있다. 이것은 어디로 분기해야할지를 알아내는 분기 예측(런타임 시에 함수 포인터의 값에 의존한다)이 가능하지 않기 때문이다.

메소드 포인터 편집

C++은 객체 지향적이어서, 클래스는 메소드들을 가질 수 있다. 정적이 아닌 멤버 함수(인스턴스 메소드)들은 암묵적인 매개변수(this 포인터)를 가지는데 이것은 동작하는 객체를 가리키는 포인터여서, 객체의 타입은 반드시 함수 포인터의 타입의 한 부분으로 포함되어야 한다. 그 후 메소드는 연산자 .* 또는 ->* (각각 객체 또는 객체를 가리키는 포인터를 위한)를 사용함으로써 그 클래스의 객체로 사용된다.

C와 C++에서 비록 함수 포인터가 간단한 주소들로 구현될 수 있지만, sizeof(Fx)==sizeof(void *), C++에서 멤버 포인터들은 가상 상속을 다루기 위해서 종종 "뚱뚱한 포인터"로 구현된다(일반적으로 간단한 함수 포인터의 크기에 비해 두세배).

C++에서 편집

C++은 "함수를 가리키는 포인터"를 함수를 다른 함수로 인자로서 전달하기 위해 사용하는데, 왜냐하면 이것들은 역참조돼서 전달될 수 없기 때문이다.

// Pointer to functions

#include <iostream>

using namespace std;

int add(int first, int second)
{
    return first + second;
}

int subtract(int first, int second)
{
    return first - second;
}

int operation(int first, int second, int (*functocall)(int, int))
{
    return (*functocall)(first, second);
}

int main()
{
    int  a, b;
    int  (*plus)(int, int) = add;
    int (*minus)(int, int) = subtract;

    a = operation(7, 5, plus);
    b = operation(20, a, minus);
    cout << "a = " << a << " and b = " << b << endl;
    return 0;
}

대안으로, C++ 표준 라이브러리 클래스 템플릿 std::function을 사용할 수도 있다. 이것의 인스턴스는 함수 객체이다.

#include <iostream>
#include <functional>

static double derivative(const std::function<double(double)> &f, double x0, double eps)
{
    double eps2 = eps / 2;
    double lo = x0 - eps2;
    double hi = x0 + eps2;
    return (f(hi) - f(lo)) / eps;
}

static double f(double x)
{
    return x * x;
}

int main()
{
    double x = 1;
    std::cout << "d/dx(x ^ 2) [@ x = " << x << "] = " << derivative(f, x, 1e-5) << std::endl;
    return 0;
}

C++에서 멤버 함수들에 대한 포인터 편집

이것이 C++이 클래스나 구조체의 멤버 함수들을 다룰 때 어떻게 함수 포인터를 사용하는지에 대한 방법이다. 이것들은 객체 포인터나 this 호출을 사용해서 작동된다. 이들은 당신이 오직 그 클래스의 멤버들만을 호출할 수 있을 때 타입 안전하다. 다음의 예시는 멤버 함수를 가리키는 포인터를 위한 typedef의 사용을 보여준다. 정적 멤버 함수들을 가리키는 함수 포인터들은 대개 포인터나 this 호출이 필요하지 않기 때문에 전통적으로 'C' 스타일로 만들어진다.

#include <iostream>
using namespace std;

class Foo
{
public:
    int add(int i, int j)
    {
        return i+j;
    }
    int mult(int i, int j)
    {
        return i*j;
    }
    static int negate(int i)
    {
        return -i;
    }
};

int bar1(int i, int j, Foo* pFoo, int(Foo::*pfn)(int,int))
{
    return (pFoo->*pfn)(i,j);
}

typedef int(Foo::*Foo_pfn)(int,int);

int bar2(int i, int j, Foo* pFoo, Foo_pfn pfn)
{
    return (pFoo->*pfn)(i,j);
}

typedef int(*PFN)(int);

int bar3(int i, PFN pfn)
{
    return pfn(i);
}

int main()
{
    Foo foo;
    cout << "Foo::add(2,4) = " << bar1(2,4, &foo, &Foo::add) << endl;
    cout << "Foo::mult(3,5) = " << bar2(3,5, &foo, &Foo::mult) << endl;
    cout << "Foo::negate(6) = " << bar3(6, &Foo::negate) << endl;
    return 0;
}

같이 보기 편집

각주 편집

  1. Andrew J. Miller. “Fortran Examples”. http://www.esm.psu.edu/~ajm138/fortranexamples.html. 2016년 1월 21일에 확인함. 
  2. “The Function Pointer Tutorials”. http://www.newty.de/: logo. 2011년 5월 16일에 원본 문서에서 보존된 문서. 2016년 1월 21일에 확인함. Function Pointers are pointers, i.e. variables, which point to the address of a function 
  3. “The Function Pointer Tutorials”. http://www.newty.de/: logo. 2011년 5월 16일에 원본 문서에서 보존된 문서. 2016년 1월 21일에 확인함. Important note: A function pointer always points to a function with a specific signature! Thus all functions, you want to use with the same function pointer, must have the same parameters and return-type! 
  4. “Expertise: Intermediate Language: C++: Use Functor for Callbacks in C++”. http://www.devx.com/: DevX.com. 2005년 1월 31일. 2016년 1월 21일에 확인함. If you want to use a member function as a callback function, then the member function needs to be associated with an object of the class before it can be called. In this case, you can use functor [with an example on this page]. 

외부 링크 편집