[C++] 8. 함수포인터, 멤버함수 포인터
2011. 6. 1. 23:00ㆍC++
퍼온 자료이나, 원본주소를 알지 못하겠군요.
---------------------------------------------------
함수포인터, 멤버함수 포인터
C++ 2011/03/09 14:46 함수 포인터
함수 포인터를 선언하는 형식: 리턴타입 (* 변수명)(인수목록)
int Function(int a) {
...
}
int main() {
int (*pf)(int) = Function; // 이런 초기화및 대입이 가능한 이유는 함수명은 함수의 시작
// 번지를 나타내는 포인터 상수이기 때문이다. 배열명과 같음.
// 함수 이름 자체가 포인터 타입이므로 &Function으로 쓸 필요 없다.
pf(); // 함수 호출. 엄격하게 따지면 에러이지만, 컴파일러가 이미 함수 포인터라는 걸
// 알고 있기 때문에 굳이 (*pf)라고 하지 않아도, pf가 가리키는 함수를
// 호출하라는 것을 알수 있기 때문에 모호하지 않다.
(*pf)(); // 동일한 함수 호출.
}
void Function() {
std::cout << "abc" << std::endl;
}
int* Temp(int*, double) {
std::cout << "Temp" << std::endl;
return NULL;
}
int main() {
typedef void (*pf)(void);
typedef int* (*pf1)(int*, double);
pf p = Function;
pf1 p1 = Temp;
int a= 100;
p = (void (*)(void))p1; // 함수 포인터 캐스트
}
여기서 p는 컴파일 타임에 void (*)(void); 타입이다.
따라서, p1을 (void (*)(void))로 캐스팅 하고 p에 대입하여도, p는 void(*)(void)타입으로 인식된다.
즉, 캐스팅 후에 p는 Temp 함수의 주소를 가지고 있지만, 함수 시그니쳐는 void에 파라미터가 없다.
그러므로, p(&a, 1.1); 형태로 호출하면 컴파일 에러가 난다.
호출은 원래 타입대로 p();로 호출해야 하며, 호출시 진입점은 Temp 함수이므로 "Temp"란 메시지가 출력되게 된다.
함수 포인터 배열
int (*arpf[5])(int);
함수 포인터의 포인터
int (**ppf)(int);
함수 포인터 타입은 복잡하기 때문에 간단히 typedef로 사용하도록 하자.
typedef int (*PFTYPE)(int);
PFTYPE arpf[5]; // 위와 동일한 함수포인터 배열
PFTYPE *ppf; // 위와 동일한 함수포인터의 포인터
함수 포인터는 다른 포인터와는 달리 ++, --와 같은 연산자를 사용할 수 없으며 정수와 가감 연산도 할 수 없다. 함수는 코드 덩어리이며 이 덩어리의 크기는 가변적이고 실행중에 변경할 수없기 때문에 당연한 이야기이다.
함수 포인터 리턴
int f1(int a, double b) {
return 1;
}
int f2(int a, double b) {
return 2;
}
int (*SelectFunc(char ch))(int,double) { // 무진장 복잡하네...
if (ch == 'a')
return f1;
else
return f2;
}
void main() {
int (*fp)(int,double);
fp=SelectFunc('a');
printf("리턴된 값 = %d\n",fp(1,2.3));
}
간단히 하기 위해서 typedef를 활용하자.
typedef int (*FP)(int, double);
FP SelectFunc(char ch) {
...
}
멤버 포인터
멤버 포인터 변수란 특정 클래스에 속한 멤버만을 가리키는 포인터이다. 일반 포인터가 메모리상의 임의 지점을 가리킬 수 있는데 비해 객체 내의 한 지점만을 가리킨다는 점에서 독특하다.
class Alber {
public:
int member_variable_;
};
int main() {
int Alber::*mem_pf = &Alber::member_variable_; // 멤버 변수를 가리킨다.
/* 특정 변수의 번지를 가리키도록 하는 것이 아니라 클래스의 어떤 멤버를 가리킬 것인가
만 초기화 하는 것이므로 이 상태에서 멤버 포인터 변수에 대입되는 번지가 결정되는 것
은 아니다. 다만 가리키는 멤버가 클래스의 어디쯤에 있는지 위치에 대한 정보만을
가질 뿐이다. 클래스 전체를 하나의 작은 주소 공간으로 보고 클래스내의 멤버 위치를
기억하는 것이다. */
Alber alber;
alber.*mem_pf = 10; // alber가 포인터라면 alber->*.mem_pf;
return 0;
}
class Test;
typedef void (Test::*fpop)(int,int);
class Test {
public:
void DoCalc(fpop fp,int a,int b) {
puts("지금부터 연산 결과를 발표하겠습니다.");
printf("%d와 %d의 연산 결과 : ",a,b);
(this->*fp)(a,b); // 멤버 함수는 반드시 호출하는 객체에 대한 정보를 가지
// 는 this라는 암시적인 인수를 전달방아야 한다.
// 멤버 함수 포인터는 함수 포인터와 달리 *를 명시해야
// 한다.
puts("이상입니다.");
}
void Op1(int a,int b) { printf("%d\n",a+b); }
void Op2(int a,int b) { printf("%d\n",a-b); }
void Op3(int a,int b) { printf("%d\n",a*b); }
};
void main() {
int ch;
Test t;
int a=3,b=4;
static fpop arop[3]={&Test::Op1,&Test::Op2,&Test::Op3};
printf("연산 방법을 선택하시오. 0=더하기, 1=빼기, 2=곱하기 : ");
scanf("%d",&ch);
t.DoCalc(arop[ch],3,4);
t.DoCalc(&Test::Op1, 3, 4); // 멤버 함수 포인터는 함수 포인터와 달리 &를
// 생략할 수 없다.
}
함수 포인터를 선언하는 형식: 리턴타입 (* 변수명)(인수목록)
int Function(int a) {
...
}
int main() {
int (*pf)(int) = Function; // 이런 초기화및 대입이 가능한 이유는 함수명은 함수의 시작
// 번지를 나타내는 포인터 상수이기 때문이다. 배열명과 같음.
// 함수 이름 자체가 포인터 타입이므로 &Function으로 쓸 필요 없다.
pf(); // 함수 호출. 엄격하게 따지면 에러이지만, 컴파일러가 이미 함수 포인터라는 걸
// 알고 있기 때문에 굳이 (*pf)라고 하지 않아도, pf가 가리키는 함수를
// 호출하라는 것을 알수 있기 때문에 모호하지 않다.
(*pf)(); // 동일한 함수 호출.
}
void Function() {
std::cout << "abc" << std::endl;
}
int* Temp(int*, double) {
std::cout << "Temp" << std::endl;
return NULL;
}
int main() {
typedef void (*pf)(void);
typedef int* (*pf1)(int*, double);
pf p = Function;
pf1 p1 = Temp;
int a= 100;
p = (void (*)(void))p1; // 함수 포인터 캐스트
}
여기서 p는 컴파일 타임에 void (*)(void); 타입이다.
따라서, p1을 (void (*)(void))로 캐스팅 하고 p에 대입하여도, p는 void(*)(void)타입으로 인식된다.
즉, 캐스팅 후에 p는 Temp 함수의 주소를 가지고 있지만, 함수 시그니쳐는 void에 파라미터가 없다.
그러므로, p(&a, 1.1); 형태로 호출하면 컴파일 에러가 난다.
호출은 원래 타입대로 p();로 호출해야 하며, 호출시 진입점은 Temp 함수이므로 "Temp"란 메시지가 출력되게 된다.
함수 포인터 배열
int (*arpf[5])(int);
함수 포인터의 포인터
int (**ppf)(int);
함수 포인터 타입은 복잡하기 때문에 간단히 typedef로 사용하도록 하자.
typedef int (*PFTYPE)(int);
PFTYPE arpf[5]; // 위와 동일한 함수포인터 배열
PFTYPE *ppf; // 위와 동일한 함수포인터의 포인터
함수 포인터는 다른 포인터와는 달리 ++, --와 같은 연산자를 사용할 수 없으며 정수와 가감 연산도 할 수 없다. 함수는 코드 덩어리이며 이 덩어리의 크기는 가변적이고 실행중에 변경할 수없기 때문에 당연한 이야기이다.
함수 포인터 리턴
fp의리턴타입 (*함수명)(인수목록))(fp의인수목록)
int f1(int a, double b) {
return 1;
}
int f2(int a, double b) {
return 2;
}
int (*SelectFunc(char ch))(int,double) { // 무진장 복잡하네...
if (ch == 'a')
return f1;
else
return f2;
}
void main() {
int (*fp)(int,double);
fp=SelectFunc('a');
printf("리턴된 값 = %d\n",fp(1,2.3));
}
간단히 하기 위해서 typedef를 활용하자.
typedef int (*FP)(int, double);
FP SelectFunc(char ch) {
...
}
멤버 포인터
멤버 포인터 변수란 특정 클래스에 속한 멤버만을 가리키는 포인터이다. 일반 포인터가 메모리상의 임의 지점을 가리킬 수 있는데 비해 객체 내의 한 지점만을 가리킨다는 점에서 독특하다.
class Alber {
public:
int member_variable_;
};
int main() {
int Alber::*mem_pf = &Alber::member_variable_; // 멤버 변수를 가리킨다.
/* 특정 변수의 번지를 가리키도록 하는 것이 아니라 클래스의 어떤 멤버를 가리킬 것인가
만 초기화 하는 것이므로 이 상태에서 멤버 포인터 변수에 대입되는 번지가 결정되는 것
은 아니다. 다만 가리키는 멤버가 클래스의 어디쯤에 있는지 위치에 대한 정보만을
가질 뿐이다. 클래스 전체를 하나의 작은 주소 공간으로 보고 클래스내의 멤버 위치를
기억하는 것이다. */
Alber alber;
alber.*mem_pf = 10; // alber가 포인터라면 alber->*.mem_pf;
return 0;
}
class Test;
typedef void (Test::*fpop)(int,int);
class Test {
public:
void DoCalc(fpop fp,int a,int b) {
puts("지금부터 연산 결과를 발표하겠습니다.");
printf("%d와 %d의 연산 결과 : ",a,b);
(this->*fp)(a,b); // 멤버 함수는 반드시 호출하는 객체에 대한 정보를 가지
// 는 this라는 암시적인 인수를 전달방아야 한다.
// 멤버 함수 포인터는 함수 포인터와 달리 *를 명시해야
// 한다.
puts("이상입니다.");
}
void Op1(int a,int b) { printf("%d\n",a+b); }
void Op2(int a,int b) { printf("%d\n",a-b); }
void Op3(int a,int b) { printf("%d\n",a*b); }
};
void main() {
int ch;
Test t;
int a=3,b=4;
static fpop arop[3]={&Test::Op1,&Test::Op2,&Test::Op3};
printf("연산 방법을 선택하시오. 0=더하기, 1=빼기, 2=곱하기 : ");
scanf("%d",&ch);
t.DoCalc(arop[ch],3,4);
t.DoCalc(&Test::Op1, 3, 4); // 멤버 함수 포인터는 함수 포인터와 달리 &를
// 생략할 수 없다.
}