프로그래머/CPP_강의정리

cpp 함수 포인터

미역국마싯 2021. 10. 6. 17:45

이 전까지만 공부해도 언리얼을 활용할 수 있다.

서버쪽에서는 STL이라는게 필요한데, 이를 공부하기 전 사전 지식을 쌓아보자. 또한 면접에서 STL관련 질문도 나오기 때문에 공부해두자.

 

typedef

typedef는 특정 자료형에 가명을 만드는 것이다.

typedef int DATA;
DATA number = 1;

여기서 정수형 자료형 int의 이름을 DATA로 지어주면, int 대신 DATA로 정수를 받는 변수를 만들 수 있다.

 

참고로 만들어진 선언 문법의 맨 앞에 typedef를 붙인다고 생각하는게 문법적으로 이해하기 편하다.

밑의 두 가지 중에서 무엇이 맞는 문법일까?

typedef int[20] ARRAY;

typedef int ARRAY[20];

 

정답은 밑의 코드가 맞는 문법이다. 이해하기 편한 것은 위다. int형 원소가 20개 있는 배열을 앞으로 ARRAY라고 부른다라고 생각할 수 있기 때문이다. 하지만 위 코드는 틀렸고, 밑의 코드가 맞다. 

앞서 말했듯이 typedef는 선언 문법(int ARRAY[20];)에 typedef를 앞에 붙인 것이기 때문이다.

 

이러한 개념을 함수에도 사용할 수 있다.

 

함수 포인터

먼저 함수의 이름은 함수의 시작 주소를 나타낸다는 것을 알아야 한다. 배열과 유사한 방식으로 함수를 호출하는 것이다.

하지만 함수는 배열처럼 메모리에 저장되는 것이 아니다. 함수는 컴파일될 때 함수의 내용이 code영역에 들어가고, 함수 포인터는 code영역에 있는 함수의 시작 주소를 가리키고 있을 뿐이다.

 

전역 및 정적 함수 포인터

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

int Sub(int a, int b)
{
	return a - b;
}

두개의 정수형 인자를 받고 정수를 반환하는 함수 Add, Sub가 있다고 하자. 앞서 얘기한 자료형에 가명을 씌우는 것처럼 함수에도 가명을 지어줄 수 있다.

typedef int(FUNC_TYPE)(int a, int b);

이제부터 int형 인자를 2개 받고, int형 데이터를 반환하는 함수의 틀을 FUNC_TYPE이라고 선언한다.

FUNC_TYPE* fn;

fn = Add;	// 또는 fn = Sub; 
		// &Add에서 &을 생략한 것

int result = fn(1, 2);	// == (*fn)(1, 2);

함수 포인터 fn는 FUNC_TYPE의 틀을 가진 함수의 주소를 저장할 수 있다. 따라서 Add와 Sub를 저장할 수 있으며, result는 fn이 가진 함수의 주소가 바뀌더라도 수정하지 않고 그대로 사용할 수 있다. 또한 다른 함수의 인자로 들어갈 수도 있다.

 

int (*fn)(int, int);

typedef int(FUNC_TYPE)(int, int);
FUNC_TYPE* fn;

참고로 둘 다 같은 의미지만, 첫 번째 방법이 실제로 많이 쓰인다.

 

typedef  bool(ITEM_SELECTOR)(Item* item);

Item* FindItem(Item items[], int itemCount, ITEM_SELECTOR* selector)
{
	for (int i = 0; i < itemCount; i++)
	{
		Item* item = &items[i];

		// selector가 true면 item을 반환
		if (selector(item))
			return item;
	}
	return nullptr;
}

ITEM_SELECTOR는 함수의 틀을 나타내는 가명이다. Item*를 인자로 받고 bool 값을 반환한다. 

FindItem에 함수 포인터인 selector를 인자로 받고 있는데, 여기서 함수 포인터의 사용법을 알 수 있다. 동작을 함수의 인자로 넘겨주고 싶을 때 사용하는 것이다.

 

bool IsRareItem(Item* item)
{
	return item->_rarity >= 2;
}

int main()
{
	Item item[10] = {};
	item[3]._rarity = 2;
	Item* rareItem = FindItem(item, 10, IsRareItem);
}

ITEM_SELECTOR와 같은 틀을 가진 함수 IsRareItem은 FindItem의 인자로 들어갈 수 있다. 위에서 말한 것처럼 IsRareItem은 희귀한 아이템을 찾을 때 사용하는 함수 인자다. 만약 검색한 단어와 같은 아이템, 공격력이 높이 아이템 등 다른 아이템을 찾는 함수를 넣고 싶다면, ITEM_SELECTOR와 같은 틀을 가지도록 각 함수를 정의하고 인자로 넣어주면 된다.

 

멤버 함수 포인터

fn = &Add;

위에서 함수 포인터를 살펴볼때, &을 생략했었다. 하지만 이제부터 생략하지 않는 것이 좋다.

 

class Knight
{
public:
	// 멤버 함수
	int GetHp()
	{
		return _hp;
	}
public:
	int _hp = 100;
};

이때까지 배운 함수 포인터 문법은 전역 및 정적 함수만 담을 수 있다. 하지만 객체를 기반으로 호출되는 멤버함수는 같은 방식으로 담을 수 없다.

 

typedef int(*FUNC)(int, int);		// 일반 함수 포인터
typedef int(Knight::*MEMFUNC)();	// 멤버 함수 포인터

일반적인 함수 포인터를 해석해보면,

[int(*)(int, int)] -> int형 인자를 2개 받고 int형 데이터를 반환하는 함수를

[FUNC] -> FUNC라는 이름으로 

[typedef] -> 부른다.

 

멤버 함수 포인터를 해석해보면,

[int(Knight::*)()] -> 인자를 받지 않고 int형 데이터를 반환하는 Knight의 멤버 함수를 

[MEMFUNC] -> MEMFUNC라는 이름으로

[typedef] -> 부른다.

FUNC fn;
fn = &Add;
fn(1, 2);

Knight k;
MEMFUNC mfn;
mfn = &Knight::GetHp;
(k.*mfn)();

Knight* k2 = new Knight();
((*k2).*mfn)();
(k2->*mfn)();

delete k2;

 

이때 멤버 함수 포인터는 멤버 함수의 주소를 받을 때 &을 생략할 수 없다.

동적 할당으로 k2를 만들었을 때는 k2의 객체 주소에 있는 Knight 클래스에 속한 GetHp 함수 주소를 가져와서 사용한다.

 

 

출처

https://www.inflearn.com/course/%EC%96%B8%EB%A6%AC%EC%96%BC-3d-mmorpg-1/dashboard

 

[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 - 인프런 | 강의

시리즈를 원활하게 학습하기 위한 기초적인 C++ 문법들에 대해 학습합니다. 어셈블리 언어부터 시작해서 기본 C++ 문법, STL, C++11까지 핵심적인 내용을 압축해서 다루게 됩니다., - 강의 소개 | 인

www.inflearn.com

 

'프로그래머 > CPP_강의정리' 카테고리의 다른 글

cpp 템플릿 기초  (0) 2021.10.06
cpp 함수 객체  (0) 2021.10.06
cpp 디버깅 기초  (0) 2021.09.28
cpp casting의 4가지 방법  (0) 2021.09.27
cpp 얕은 복사와 깊은 복사  (0) 2021.09.23