shepherd's Blog

[C/C++] 함수 포인터 본문

C,C++

[C/C++] 함수 포인터

shepherd.dev 2015. 9. 16. 15:15

[C/C++] 함수 포인터

 C와 C++에서 함수 포인터를 한번 살펴보겠습니다. 함수 포인터란 함수를 저장할 수 있는 포인터를 뜻합니다. 우선 다음과 같은 세가지를 예제로 살펴보겠습니다.

1.일반 함수 포인터에 멤버함수의 주소를 담기

2.일반 함수 포인터에 static 멤버변수를 만들기

3.멤버 함수를 함수 포인터에 담기


#include<iostream>

using namespace std;

typedef class _Point{

int x;

int y;

public:

//static int s;

public:

_Point(int x = 0, int y = 0, int s = 0)

{

this->x = x;

this->y = y;

//this->s = s;

}

void Print()

{

cout<<"x: "<<x<<" y: "<<y<<endl;

}

void foo()

{

cout<<"foo"<<x<<" y: "<<y<<endl;

}

static void s_foo()

{

//cout<<"s_foo"<<s<<endl;

cout<<"s_foo"<<endl;

}

}Point, *PPoint;


void g_foo(){cout<<g_foo<<endl;}


void main()

{

Point p;

// [1]

void(*fp)() = g_foo;

fp();

// [2]

fp = Point::s_foo;

fp();

// [3]

      //fp = p.foo();

//fp = Point::foo();

// [4]

      void (Point::*fp2)() = &Point::foo;

(p.*fp2)();

}


[1] 함수 포인터 선언 부분입니다. 함수의 파라미터를 맞춰 잘 선언하신 뒤, 선언과 동시에 전역 함수인 g_foo()의 주소를 넣어줍니다. 그 뒤 함수 포인터 fp를 실행하면 g_foo()가 실행됩니다. 여기까진 C에서도 동일한 방식으로 사용합니다.


[2] p의 정적함수의 주소를 함수 포인터에 넣어줍니다. 이 부분도 이상 없이 잘 실행됩니다.


[3] Point Class의 일반 멤버 함수는 포인터로 사용 가능할까요? fp에 주석 처리한 두 가지 방식으로 대입하면 실패하는 것을 보니 일반 함수 포인터로는 불가능 한 것 같습니다.. namespace를 생각해주시면 편하게 이해하실 수 있습니다. fp는 main함수에 속한 지역변수이기 때문에 Point에 대한 접근권한이 없을 것입니다. 그래서 Point의 멤버 함수에 접근할 수 없습니다.(제 생각, 제대로 찾는대로 다시 올리겠습니다.)


[4] Point의 일반 멤버 함수용 포인터 fp2를 새롭게 선언합니다. 그 뒤 Point Class의 멤버 함수의 주소를 넣어줍니다. 그 뒤 선언해둔 p의 멤버함수로 fp2를 사용합니다. 여기서 이상하다고 느끼시는 분들이 있을텐데 class와 object 메모리에 어떤식으로 적재되는지 이해하신다면 이해하기 쉬울겁니다.

 class는 structure처럼 일종의 데이터를 찍기 위한 틀입니다. 이 틀로 찍어낸 것이 object라 불리죠. 하지만 c++에서 class와 struct와의 차이는 자신의 함수를 가질 수 있다는 것에 있습니다. class를 object화 하여 memory에 적재시킬 경우 하나의 object만 사용할지 아주 많은 수의 object를 사용할지는 컴퓨터는 모릅니다. 이렇게 object를 생성할 때마다 메모리에 class의 변수와 함께 함수 내용까지 중복해서 올린다면 엄청난 메모리 낭비가 발생하겠죠. 이 때문에 애초에 함수의 코드 부분은 memory의 text영역에 들어가 있습니다. 이 text영역에서 멤버 함수의 시작 주소만 안다면 모든 object들은 함수 내용을 가질 필요가 없습니다.

 여기까지 이해하셨다면 바로 왜 Point Class 멤버 함수의 주소를 fp2에 넣어주는지 눈치 채셨을 겁니다. 바로 text 영역의 Point 멤버함수 시작주소를 fp2에 넣어준 것이죠. 그 다음부터 각 오브젝트는 함수 포인터를 사용하든 함수이름으로 함수를 사용하든 동일한 주소를 가리키게 되고, 동일한 함수를 사용하게 됩니다.

 이 개념은 기존 함수 포인터의 개념일 것입니다.(이것도 제 생각... C표준을 본게 아니라서 구동 방식과 memory 구조를 보고 적은겁니다. 틀린부분 댓글 달아주세요.)


[ex] 다음은 함수포인터를 이용한 콜백 함수 사용 예제입니다.

// callback 함수 호출

#include<iostream>

using namespace std;

class Good;

typedef void(*CALLPROC)(void *param);

typedef void((Good::*CALLPROC2))(void *param);

void setFunction(int n, CALLPROC f, void *param)

{

for(int i=0;i<n;i++)

{

(*f)(param);

}

}

void foo(void *param)

{

char *msg = (char *)param;

cout<<msg<<endl;

}

class Good{

public:

static void goo(void* param)

{

char *msg = (char *)param;

cout<<msg<<endl;

}

void foo(void* param)

{

char *msg = (char *)param;

cout<<msg<<endl;

}

};

void setFunction2(int n, CALLPROC2 f, void *param)

{

Good g;

for(int i=0;i<n;i++)

{

(g.*f)(param);

}

}

void main()

{

setFunction(5,foo,"Hello");

cout<<endl;

setFunction(5,&Good::goo,"Hello");

cout<<endl;

//void (Point::*fp2)() = &Point::foo;

//fp = p.foo();

//fp = Point::foo();

//(p.*fp2)();

setFunction2(5,&Good::foo,"Hello");

}


'C,C++' 카테고리의 다른 글

std::tie, std::tuple  (0) 2019.08.04
[c++] namespace detail  (0) 2018.11.28