본문 바로가기
C++

[C++]함수 포인터 와 Class

by Junk_Seo 2018. 2. 28.
반응형

함수 포인터

함수 포인터는 함수의 시작 주소 값을 가리키는 포인터 변수입니다.

함수 포인터 변수 형식

반환형 (*함수 포인터 변수 이름)(인수 목록); 

의 형태를 가집니다.

코드로 보자면 다음과 같습니다.

int func(int nData1, int nData2);

int main()
{
	int (*pFunc) (int, int) = nullptr;
	pFunc = &func;
	printf("%d \n", (*pFunc)(2,5));

    return 0;
}

위 코드를 보시면 반환형이 int형이고 인자로 int형 변수 2개를 받는 func() 함수가 있습니다.

그리고 이 함수를 가리킬 수 있는 함수 포인터 변수 int (*pFunc)(int , int)를 선언하고 nullptr로 초기화하였습니다.

(포인터 변수이므로 nullptr로 초기화)

그리고 이 함수 포인터 변수에 "pFunc = &func;"로 함수 포인터 변수 pFunc가 func 함수를 가리키도록 하고 있습니다.

이 함수 포인터 변수를 사용할 때에는 7행처럼 그냥 함수를 사용하는 것과 비슷하게 사용합니다. 

함수 포인터 배열

함수 포인터 배열은 형식이 같은(반환형과 인수목록이 같은) 함수 여러 개를 함수 포인터 배열을 사용하여 관리할 수 있습니다.

 

형식은 다음과 같습니다.

반환형 (*함수 포인터 변수 이름[배열 길이])(인수 목록);

코드로 보자면 다음과 같습니다.

int add(int nData1, int nData2);
int mul(int nData1, int nData2);
int sub(int nData1, int nData2);

int main()
{
	int(*pFunc[3])(int, int) = {};
	
	int nData1 = 0;
	int nData2 = 0;
	int nSelect = 0;

	pFunc[0] = &add;
	pFunc[1] = &mul;
	pFunc[2] = ⊂

	scanf_s("%d %d %d", &nData1, &nData2, &nSelect);

	printf("%d \n", (*pFunc[nSelect])(nData1, nData2));

    return 0;
}

반환형이 int이고 int형 변수 2개를 인수로 갖는 함수 add, mul, sub 3개가 있습니다. 이를 7행에서 함수 포인터 배열을 선언하여 관리하고 있습니다. 

Class 멤버 함수 포인터

class와 함수 포인터가 결합하면 좀 어렵고 헷갈려하실 겁니다.(저도요...)

일단 코드와 함께 보겠습니다.

void func1();

class C_FUNC
{
private:
#1	void(*m_pFunc1)();
#2	void(C_FUNC::*m_pFunc2)();

public:
	void func2();
};

위 코드를 보시면 #1과 #2 가 있습니다.

둘 다 틀린 것은 아니지만 의미상의 차이가 있습니다.

 

#1의 경우는 전역 함수를 가리키는 함수 포인터 변수입니다. 즉 위 코드에서 전역 함수인 func1() 함수를 가리킬 수 있는 포인터 변수입니다.

#2의 경우는 멤버 함수를 가리킬 수 있는 함수 포인터 변수입니다. 즉 위코드에서 멤버 함수인 func2() 함수를 가리킬 수 있는 포인터 변수입니다.

 

이렇게 차이가 나는 이유는 바로 namespace 때문입니다. class의 멤버들은 변수 또는 함수 이름 앞에 namespace가 생략되어 있다는 것을 알고 계셔야 합니다. 

위 코드에서 class멤버 함수인 func2() 함수는 원래 "void C_FUNC::func2()"의 모양을 취해야 하지만 class 안에 있기 때문에 namespace가 생략되어 있는 것이지요. 

header 파일에 class를 선언하고 cpp 파일에 멤버 함수를 구현할 때 멤버 함수의 앞에 class의 namespace가 붙는 이유와 같습니다. class안에 있을 때에는 생략이 가능하지만 밖으로 나가게 되면 namespace를 명시해 주어야 하는 것이지요.

 

즉, 위 코드에서 class의 멤버 함수인 func2()의 정확한 모양은 "void C_FUNC::func2()"입니다. 그렇기 때문에 class 멤버 함수 포인터 변수가 class 멤버 함수를 가리키기 위해서는 #2처럼 namespace까지 명시해주어야 합니다. func1() 함수와 func2() 함수가 겉으로 보기에는 같은 형식의 함수처럼 보이지만 func2() 함수의 경우 namespace가 생략되어 있는 것이니까요.

 

그리고 class 멤버 함수를 가리키는 함수 포인터 변수의 경우 다음과 같이 사용합니다.

void C_FUNC::init()
{
	m_pFunc1 = &func1;
	m_pFunc2 = &C_FUNC::func2;
}

3행의 전역 함수를 가리키는 함수 포인터 변수의 경우 "&함수이름"으로 사용하시면 되지만 4행의 class 멤버 함수를 가리키는 함수 포인터 변수의 경우는 멤버 함수를 가리킬 때 그 함수의 namespace를 명시해 줍니다.

 

그리고 이 함수 포인터 변수를 가지고 함수를 호출하는 방법은 다음과 같습니다.

void C_FUNC::show()
{
	//(C_FUNC::*m_pFunc2)();
	(this->*m_pFunc2)();
}

지금까지 함수 포인터 변수를 보면서 사용법이 3행의 주석 친 부분처럼 사용할 것이라는 생각이 많이 들겠지만, 저렇게 하면 error가 발생하게 됩니다. 반드시 4행처럼 this 포인터를 사용해서 사용해야 합니다.

그냥 단순 문법으로 이해가 아닌 암기가 필요한 부분입니다....

 

이렇게 사용하는 이유가 맞는지는 모르겠지만 "*" 연산자는 namespace 앞에 와야 한다고 합니다. 하지만 위 주석 친 코드처럼 class에서 함수 포인터 변수를 사용하게 되면 "*" 연산자가 namespace 뒤에 오게 되는 문제가 발생하게 되고 이를 해결하고자 this 포인터를 사용하였다.라고 합니다. 

정확히 맞는지는 모르겠습니다만 이유가 어떻든 이 부분은 암기입니다.

 

 

<예제>

class C_CALCULATION
{
public:
	enum class E_CALCULATION {
		E_ADD,
		E_MUL,
		E_SUB,
		E_MAX
	};

private:
	int m_nData1;
	int m_nData2;
	int m_nResult;

	void (C_CALCULATION::*m_pFunc[E_CALCULATION::E_MAX])();

private:
	void add();
	void mul();
	void sub();

public:
	C_CALCULATION();
	void init();
	void setData(int nData1, int nData2);
	int getResult();
	void update(E_CALCULATION eType);
};

함수 포인터 배열 m_pFunc 이 선언되어 있습니다. 

enum class의 경우 함수 포인터 배열에서 함수 구분을 위한 것입니다.

 

void C_CALCULATION::add()
{
	m_nResult = m_nData1 + m_nData2;
}

void C_CALCULATION::mul()
{
	m_nResult = m_nData1 * m_nData2;
}

void C_CALCULATION::sub()
{
	m_nResult = m_nData1 - m_nData2;
}

C_CALCULATION::C_CALCULATION() :
	m_nData1(0),
	m_nData2(0),
	m_nResult(0),
	m_pFunc{}
{
}

void C_CALCULATION::init()
{
	m_pFunc[(int)C_CALCULATION::E_CALCULATION::E_ADD] = &C_CALCULATION::add;
	m_pFunc[(int)C_CALCULATION::E_CALCULATION::E_MUL] = &C_CALCULATION::mul;
	m_pFunc[(int)C_CALCULATION::E_CALCULATION::E_SUB] = &C_CALCULATION::sub;
}

void C_CALCULATION::setData(int nData1, int nData2)
{
	m_nData1 = nData1;
	m_nData2 = nData2;
}

int C_CALCULATION::getResult()
{
	return m_nResult;
}

void C_CALCULATION::update(E_CALCULATION eType)
{

	(this->*m_pFunc[(int)eType])();
}

int() 함수에서 함수 포인터 배열 m_pFunc에 각 각 함수를 가리키게 하고 있습니다. 

update() 함수를 통해 인자로 들어온 eType에 따라 함수 포인터 배열에서 함수를 호출합니다.

 

int main()
{
	C_CALCULATION cCal;
	cCal.init();
	cCal.setData(10, 20);
	cCal.update(C_CALCULATION::E_CALCULATION::E_ADD);
	printf("%d \n", cCal.getResult());
    return 0;
}

C_CALCULATION class 변수 cCal을 선언하고, init() 함수를 통해 함수 포인터 배열을 설정합니다. 

setData() 함수를 통해 값을 설정하고, update함수에 인자로 C_CALCULATION::E_CALCULATION::E_ADD를 주어 add() 함수를 호출하여 결과를 세팅하도록 합니다.

그리고 printf()를 통해 결과를 호출합니다.

반응형

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

[C++]랜덤함수 rand_s() 함수  (0) 2018.03.11
[C++]함수 템플릿, 클래스 템플릿  (0) 2018.03.05
[C++]static 정적 변수와 Class  (0) 2018.02.27
[C++]friend 키워드  (0) 2018.02.21
[C++]Class의 기본 형태  (0) 2018.01.30