본문 바로가기
공부

C++ 함수 포인터 / 멤버 함수 포인터

by MY블로그 2022. 11. 30.

기존수업에서 함수는 코드영역에 저장되고 컴파일 타임에 미리 생성이되며 주소를 갖는다고 배웠다.

이번에조사할 과제는 그 함수에 관련된 조사이다.

함수는 주소를 가지고있다 했고.. 포인터는 주소를 담는다고 했고.. 과연 함수 포인터가 무엇인지 알아보자.

 


 

함수 포인터 ?

함수는 다른 변수들 처럼 '주소' 를 가지고 있다.

함수의 주소를 포인터 변수에 저장하여 사용이 가능한 것을 '함수 포인터' 라고 한다.

 

아래의 예제를 통하여 선언의 형태를 확인하자.

●코드 예제1
int (*f)(int, int);
// int			리턴타입
// (*f)			변수의 이름 f 를 함수포인터로 선언
// (int, int)	함수의 매개변수

위처럼 선언하면 함수의 주소값을 저장할 수 있는 ' f ' 라는 함수 포인터가 만들어지게 된다.

이 ' f ' 라는 포인터 안에 함수의 ' 주소 ' 를 저장하여 사용이 가능하다.

 

사용방법은 코드 예제2 를 참조하자!

●코드 예제2
#include<iostream>
using namespace std;

int add(int a, int b)
{
	return(a + b);
}

int main()
{
	int (*f)(int, int);

	f = add; // 전역에있는 함수 add 를 함수포인터 'f' 에 저장!
	
	cout << f(1, 2) << endl; // add 함수를 f가대체하고 매개변수는 int a, int b !!
	// 반환값은 a + b 이므로 출력은 3이될것으로 예상된다!

	return 0;
}
●코드 예제2 출력결과

간단한 예제를 통하여서보니  조금 이해가된다.

이제 조금더 어려운 예제를 보도록 한다.

 


 

매개변수로 사용 할 수 있는 함수 포인터!

제목만봐도 조금 어려울 것 같다...

함수 포인터도 다른 변수처럼 매개변수로 사용이 가능하다고 한다..

아래의 예제를 보도록 하자.

●코드 예제3
#include<iostream>
using namespace std;

int add(int a, int b)
{
	return(a + b);
}

void print_odd(int a, int b, int (*f)(int, int)) // 매개변수에 함수 add 사용!
{
	int ret = f(a, b);

	if ((ret % 2) == 1) // a + b 를 2로 나눈 나머지가 1이 아니라면(짝수라면)
		cout << ret << endl; // 그 수를 출력

	else // 짝수라면 홀수가 아니다 출력
		cout << "홀수가 아니다!" << endl; 
}

int main()
{
	print_odd(2, 4, add);
	print_odd(2, 3, add);

}
●코드 예제3 출력결과


 

함수 포인터의 const

요새 너무 자주보는 것 같은 const ... 또나왔다.

갑자기 const 와 static 의 개념이 햇갈려버렸다.

잊지않게 명시해두자.

 

const

  • 대상을 변하지않는 '상수' 로 선언한다.
  • 한번 값을 할당 하면 다른곳에서 변경이 불가능하다!

static

  • '정적' 선언 ( 자동 주기 에서 -> 정적 주기로 변경된다)
  • 변수에 선언시 스코프가종료되도 남아있으며 프로그램 종료시 소멸된다
  • 한번만  초기화 할 수 있다.

이것저것 햇갈리는게 너무 많은 것 같다.. 

이제 다시 함수 포인터의 const 부분으로 넘어가자.

위에서 봤다싶이 상수로 선언 시키는 것과 관련이 있는 것 같다. 

함수포인터도 다른 변수들처럼 ' const ' 를 사용 할 수 있다고 한다.

아래 코드를 통하여 확인하자

●코드 예제4
#include<iostream>
using namespace std;

int add(int a, int b)
{
	return(a + b);
}

int main(void)
{
	int (* const f)(int, int) = add;
	// 참조 연산자 *옆에 const를 붙여주면 함수포인터 f를 상수화 시킬 수 있다!

	cout << f(1, 2) << endl;
	cout << f(3, 4) << endl;
	cout << f(6, 7) << endl;

}
●코드 예제4 출력결과

 


 

class 멤버함수의 함수포인터 사용

그간 공부하면서 들어봤던것들 다나오는 느낌이다... 이번에는 class의 차례 같다.

class의 멤버함수도 함수포인터로 사용이 가능하다.

그러나 class 내부의 멤버함수는 ' 객체 ' 를 통해서만 출력이 가능하며

함수포인터의 선언 또한 해당 클래스의 ' 범위 ' 내에있는 함수포인터라고 명시해야 사용이 가능하다.

 

●코드 예제5
#include<iostream>
using namespace std;

class Sample
{
public:
	int add(int a, int b) // 클래스 Sample 안에 멤버함수 add 가 있다!
	{
		return (a + b);
	}
};

int main(void)
{
	Sample sample; // Sample 클래스를 인스턴스화 시킨뒤 객체생성
	int (Sample::*f)(int, int) = &Sample::add; // 와....
	// 함수포인터 'f' 는 Sample 멤버함수를 사용한다고 'Sample::' 처럼 명시한다.
	// 함수의 주소 또한 Sample 의 멤버함수임을 명시한다.
	// 함수 주소를 가져올때는 '&' 레퍼런스 연산자를 통하여 주소를 가져온다.

	cout << (sample.*f)(1, 2) << endl;
	cout << (sample.*f)(3, 4) << endl;
	cout << (sample.*f)(5, 6) << endl;
	// 함수포인터의 사용시 해당 클래스의 객체를 통해서 사용한다.
	// class 멤버함수 내에서 함수포이넡를 사용한다면 this 를 통하여 접근이 가능하다.
	// 접근은 멤버 포인터 연산자인 '.*' 또는 '->*' 연산자로 접근해야 한다.
}
●코드 예제5 출력결과

 


 

 

static을 이용한 멤버함수의 접근

위에서 const static 에 혼란이있다했는데 ... 여기 나왔다. 이렇게된거 한번더 확인하자.

 

const

  • 대상을 변하지않는 '상수' 로 선언한다.
  • 한번 값을 할당 하면 다른곳에서 변경이 불가능하다!

static

  • '정적' 선언 ( 자동 주기 에서 -> 정적 주기로 변경된다)
  • 변수에 선언시 스코프가종료되도 남아있으며 프로그램 종료시 소멸된다
  • 한번만  초기화 할 수 있다.

class 에서 멤버함수에 ' static ' 을 사용하면 class의 ' 이름 ' 으로 해당 함수에 접근이 가능하며

특정 객체에 ' 귀속 ' 되는 것이 아니다.

때문에 class의 이름을 통해서 접근만 해주면 일반 함수포인터와 동일하게 사용이 가능하다.

 

코드가 조금 길지만 이해할수 없는게 아니다! 잘 보도록 하자!

●코드 예제6
#include<iostream>
using namespace std;

class Sample // 더하기 클래스
{
public:
	static int add(int a, int b)
	{
		return (a + b);
	}
};

class Sample2 // 빼기 클래스
{
public:
	static int sub(int a, int b)
	{
		return (a - b);
	}
};

class Sample3 // 나누기 클래스
{
public:
	static int div(int a, int b)
	{
		return (a / b);
	}
};

class Sample4 // 곱하기 클래스
{
public:
	static int mul(int a, int b)
	{
		return (a * b);
	}
};

int main(void)
{
	//함수포인터의 배열을 선언 후, class 이름을 통해서 함수의 주소를 저장한다.
	int(*f[4])(int, int) = // 함수포인터에 배열선언 신기하다
	{
		&Sample::add, // f[0]
		&Sample2::sub, // f[1]
		&Sample3::div, // f[2]
		&Sample4::mul // f[3]
	};

	for (int i = 0; i < 4; i++) // 0 1 2 3 까지
	{
		cout << f[i](1, 2) << endl;
		// 출력은 일반 함수 포인터와 동일하다.
	}
}
●코드 예제6 출력결과

조금은 복잡할것같은 코드를 사용하여 나온 결과가 사칙연산이라 조금 김이 빠지지만 신기하다.

한번에 모든 것을 이해할 수 없다. 모르더라도 한번씩보고 보는동안 원리를 이해하려고 노력 하자.

댓글