함수포인터

2006. 11. 23. 22:33
함수포인터는

일반적으로 C에서 쓰이는 문법과

C++ 즉 멤버함수 포인터를 선언 사용하는 문법이 상당히 차이가 있습니다



그래서 많이들 혼동하시는 경향이 있는데

그냥 서로 다른문법으로 사용을 해야하는구나 라고 생각하시면 이해에 좀더 도움이 될것 같습니다



우선 질문하신 내용을 살펴보기전에 배경지식을 위해서

C에서의 함수포인터 사용법을 잠깐 살펴보고 넘어가겠습니다

void pr(){...}

int main()

{

   void(*p)();        // 함수포인터 선언

#1    p = pr;              // 맞음

#2    p = ≺            // 맞음

#3    p()                   // 맞음

#4    (*p)();              // 맞음

}



#1도 맞고 #2도 맞다니 모순이죠 ^^ #3과 #4의 경우도 마찬가지이구요

위와같은 모순된 문법이 탄생하게된 계기는

많은 C개발자들의 함수포인터에 대한 서로다른 주장을 펼치는 바람에 생겨나게 되었다고 합니다



함수의 이름은 이름자체가 컴파일러입장에서 보면 포인터이기 때문에

p라는 함수포인터에 pr이라는 함수의 이름을 그냥 대입하는것이 맞다 (즉 p=pr이 맞다라고 주장)

그러므로 사용할때도 함수도 그냥 pr() 이렇게 사용하므로 p(); (#3번) 이렇게 사용하는것이 맞다



아니다 함수이름의 경우

함수를 가르키는 포인터 변수에 대입할때 &주소연산자를 붙여주지 않으면 오히려 혼동의 여지가 있다

그래서 붙여주는 것이 낫다 (즉, p=&pr이 맞다)

그리고 사용할때도 &연산자를 사용해 대입했므로 (*p)() 이렇게 사용하는것이 맞다라고 서로 모순된 주장을 하였죠



결과는 두 주장 모두 일리가 있으므로 둘다 수용한다 ㅡ_ㅡ;;;; 라는 쪽으로 결론이 났습니다

참 우습죠

가장 논리적인 기계중 하나인 컴퓨터를 다루는 C언어에 서로 모순되는 문법이 존재하다니...



여담으로 어느 책에서 읽은것인데

이세상에서 서로 모순을 가진 두가지 명제를 받아들이고 이해할수 있는 동물은 인간이 유일하다라는 말이있답니다 ^^



이제 질문의 본론으로 들어가서

멤버함수 포인터 먼저 선언을 살펴보겠습니다



#4 void foo(){.....}



class A

{

private:

#1    void (A::*fpr[3])();  

#2    void (*fpr2)();

public:

#3     void print1(){....}

}



#1번 선언때문에 놀라셨다고 했는데

#2번 선언이 필요할때도 있다는걸 아시면 아마 이해가 가실겁니다



#1번을 설명하기 전에 #2번을 설명을 드리겠습니다

클래스를 설계하다보면 멤버변수로 외부 함수를 가르키는 포인터가 필요할수 있습니다

fpr2라는 함수포인터는 C스타일의 함수포인터 변수 입니다

즉 어떤 클래스안에 존재하는 함수를 가르키는 포인터 변수가 아니라 외부에 선언된 함수를 가르키는 포인터 변수죠



여기서 서로 다른 문법의 필요성이 대두됩니다

void A::Init()

{

   fpr[0] = foo;   // 에러 발생

   fpr2 = foo;      // 외부에 선언된 함수(c스타일)를 가르킬수 있음    

}



그렇다면 다른 문법이 필요하긴 한데 어떻게 선언하고 사용할것인가??

원래 클래스 안에서 선언된 변수는 클래스 범위연산자가 생략되어있습니다

그래서 클래스 범위연산자를 붙이나 안붙이나 의미는 동일합니다

class A

{

   int a;     // 아래 선언과 동일한 의미라는 것이죠

   int A::a; // 위와 동일한 선언

}



하지만 위에서처럼 두가지의 서로다른 함수포인터를 가르키는 멤버변수가 필요하기 때문에

함수포인터 앞에 클래스범위 연산자를 붙이는 것과 그렇지 않은 경우 서로 다른 의미를 가지는 문법이 생겨나게 된것입니다

class A

{

   void (*p)();         // 서로 다른 의미의 선언 (외부 함수를 가르킬수 있는 포인터 변수)

   void (*A::p)();    // 참고로 이선언은 위의 void (*p)();의 선언과 동일합니다 아래와 혼동하지 마시길...

   

   void (A::*p)();     // 서로 다른 의미의 선언 (A클래스의 멤버 함수를 가르킬수 잇는 포인터 변수)

}



자 이제 대입을 살펴보겠습니다

void A::Init()

{

#1    (fpr[0])=print1;       //정상작동

#2    (fpr[0])=A::print1;   //정상작동

#3    (fpr[0])=&A::print1; //정상작동

#4    (fpr[0])=&print1;     // 에러 발생



#5     fpr2 = foo;  // 정상 작동

}



#1번의 print1은 A::라는 클래스 범위연산자를 앞에 붙여주지 않았지만 생략된것이나 마찬가지 이므로

#2번과 동일한것을 알수있습니다

#2번과 #3번은 서로 모순이지만 위에서 언급한 C의 함수포인터와 똑같은 문법이 적용되고 있음을 알수 있습니다



문제는 #4번인데 저도 처음에 이놈때문에 골치가 아팠는데

결국은 멤버함수 포인터에서는 저런문법은 안된다라고 외웠습니다 ㅡ_ㅡ;;;

어떻게 보면 이상할것도 없는게 문법이라는게 제정한 사람들의 몫이므로

앞서 서로 모순된 문법이 존재하는것 처럼 오히려 문법을 암기가 아니라

이해를 하려고 한다는 자체가 이상할수도 있다는 생각입니다

(혹시나 #4번에 관해 논리적인 다른 내용을 알고 계신분이 있다면 댓글을 달아주셨으면 좋겠네요 ^^)

참고로 예전에 제가 읽었던 책에서는 #3번을 권장했었던 걸로 기억이 납니다



자 이제 사용법에 관한 내용입니다

void A::Show()

{

#1    (this->*fpr[0])();  //이렇게 해야 정상작동..

#2    (A::*fpr[0])();;     //문법오류



#3        fpr2();              //정상 작동

#4        this ->*fpr2()    // 문법 오류

}



#1번을 보면 멤버함수를 가르키는 포인터 변수를 사용할때는 .*  또는 ->* 이라는 멤버내용 참조연산자가 쓰인다는걸 알수있습니다

즉 #2번 처럼 쓰면 안됩니다

#3번을 보면 C스타일의 멤버함수를 가르키는 포인터 변수는 그냥 사용하셔도 되는걸 알수 있습니다



이것은 문법입니다

이해가 가면 좋겠지만 이해가 되지 않을때는 외워야 한다는 이야기죠

이말은 C++에서는 C의 함수포인터와는 다르게

멤버함수 포인터를 위해 .*과 ->*이라는 멤버내용 참조연산자를 준비해 놓았다는 이야기 입니다

   











지금까지 함수포인터에 관한 많은 부분을 알아봤는데

사실 한가지 또다른 주제가 있습니다



위의 내용은 클래스 내에서 또는 클래스에 정의된 멤버함수내에서 (Init, Show)

선언 사용하는 법을 다뤘는데



클래스 밖에서 사용하는 방법은 약간의 차이가 또 존재 한답니다 ㅜ.ㅜ



오늘은 글이 너무 길어져 이만 생략하기로 하구요...

함수포인터의 이해에 조금이나마 도움이 되셨으면 하네요





마치기전

마지막으로 함수 이중포인터의의 동적 배열 할당은



typedef void (*FPTR)();

void (**p)() = new FPTR[4];

void (*(*p)[4])() = new FPTR[4][4];



이런식으로 하시면 되겠습니다

'개인창고 > ...' 카테고리의 다른 글

매의 발톱단  (0) 2007.05.13
어떻게 살아야 100점 짜리일까?  (0) 2007.04.26
Windows Vista Enterprise K x64 호환성 목록  (0) 2007.04.23
Dream Girls  (0) 2007.02.13
미녀는 괴로워..  (0) 2007.02.05

밥짓는아이 개인창고/...