0. 포인터란

  • 메모리 주소를 가진 변수
  • 변수의 주소를 알려주는 &(엠퍼센트) 연산자와 주소를 알면 원본에 접근할 수 있는 *(애스터리스크) 연산자와 함께 조합하여 사용하는 경우가 많음
  • 원본의 변경이 가능한 만큼 const 지정자를 적절히 사용하여 코딩하는 습관이 필요

 

1. 허상 포인터

  • 포인터 변수가 특정 주소를 가리키고 있으나 실제 메모리에 할당된 공간이 아닌 경우
  • 동적할당 후 메모리 해제를 하였으나 동적할당했던 메모리의 주소를 초기화 하지 않는 경우 주로 발생하는 문제
  • delete와 함께 포인터를 nullptr로 초기화하는 이유가 허상 포인터(댕글링 포인터)를 막기 위함
int* pa = new int;
delete pa;
*pa = 10;  //error
std::cout << *pa << std::endl;  //error

 

2. 함수 포인터

  • 함수에도 주소가 있기 때문에 함수 포인터로 특정 함수의 주소를 알면 사용할 수 있음
  • 함수의 주소는 메모리의 위치 상 코드영역 이라고 함
//원시적인 사용법
int Test(float a) {}  //반환 int, 매개변수 float을 받는 함수

int (*FuncPtr)(float) = Test;  //반환 값과 매개변수를 맞춰 작성 필수
//(*FuncPtr)을 괄호로 감싼 이유 : 괄호가 없다면 int*를 반환하는 함수의 전방선언으로 인식
//Test는 주소를 알려주는 & 연산자가 생략된 것 -> 함수 이름은 주소 연산자 없이도 주소를 나타냄

FuncPtr();  //함수 포인터를 사용하여 Test 함수를 호출 가능
//마찬가지로 (*FuncPtr)();이 명확한 표기이나 암시적으로 위와 같이 표기를 많이 함(함수 이름이 주소의 의미를 담기 때문)
  • typedef, using 키워드를 사용하여 함수 포인터에 강제되는 규격을 하나의 자료형처럼 쓸 수 있음
  • 위 예제에선 반환형이 int, 매개변수가 float인 규격이 강제되는데 이 규격에 맞춘 자료형을 만든다고 생각하면 됨
typedef int (*MyFuncPtr)(float);  //함수 포인터를 자료형처럼 쓸 수 있게 만들어주는 키워드
MyFuncPtr TestFuncPtr;  //반환은 int, 매개변수는 float의 함수를 담을 수 있는 함수 포인터
using MyFuncPtr = int(*)(float);  //위 typedef 예제와 동일한 기능
MyFuncPtr TestFuncPtr;
  • #include <functional>의 function 클래스를 통해 함수 포인터 간편 사용 가능
#include <iostream>
#include <functional>

void PrintHello(int Num)
{
	std::cout << "Hello" << Num << std::endl;
}

int main()
{
	std::function<void(int)> TestFunctionPtr = PrintHello;
	TestFunctionPtr(1);

	TestFunctionPtr = [](int Num) {
		std::cout << "HelloLambda" << Num << std::endl;
	};

	TestFunctionPtr(2);

	return 0;
}
  • std::function에 꺽쇠 괄호 한에 리턴 값, 매개변수 정보를 넣어주면 됨
  • Lambda를 이용한다면 즉각적으로 함수 포인터의 값을 채워줄 수도 있음

 

3. 멤버함수에 함수 포인터 적용

  • 멤버함수를 함수포인터에 담을 수 있음을 알고 그 규격을 인지해야 함, 기존의 방식과는 조금 다르게 함수포인터의 주소를 알기 위해 &의 명시, 함수포인터의 주소에 접근하기 위한 *가 명시되어 사용 됨
  • 함수포인터를 사용하는 Raw한 방법과 std::function을 사용하는 방법이 미묘하게 다름
  • 멤버함수를 부르기 위해선 어떤 인스턴스에서 호출할지를 분명히 해야한다는 점은 분명하고, 그 인스턴스를 어떻게 전달하느냐의 차이
#include <iostream>
#include <functional>

class Test
{
public:
	Test() {}

	void PrintHello()
	{
		std::cout << "Hello" << std::endl;
	}
};

int main()
{
	Test T;
	Test* PointerT = new Test;

	void (Test:: * MemberFuncPtr)() = &Test::PrintHello;
	std::function<void(Test&)> MemberFuncPtr2 = &Test::PrintHello;

	(T.*MemberFuncPtr)();
	(PointerT->*MemberFuncPtr)();
	MemberFuncPtr2(T);

	delete PointerT;

	return 0;
}

 

4. 델리게이션

  • '위임'의 의미를 담고 있는 단어
  • 언리얼 엔진에선 함수 포인터를 '델리게이트' 라는 이름의 클래스로 만들어서 사용
  • 함수 포인터가 '위임'의 의미를 담고 있다고 봐도 무방
  • '위임'이란 ? : 게임을 예로 들면 마우스 좌클릭을 했을 때 공격이 나간다 쳤을 때 Character::Attaack() 이라는 함수가 마우스 클릭과 동시에 호출 될 것이다. 이 때 Weapon이 있다면 Weapon의 Attack()을 불러주는 것이 '위임'이라고 할 수 있겠다.

 

5. CallBack 함수

  • 함수 포인터를 인자로 넘기는 상황 혹은 특정 이벤트에 불려지는 함수
  • 함수 포인터를 인자로 넘긴다는 건 그 함수 내부에서 인자로 넘어간 함수를 불러준다는 것을 의미
  • 특정 이벤트란 위에서 들었던 예시로 마우스 클릭 시 Attack()이라는 함수가 불리는 것

'C++ > 키워드 정리' 카테고리의 다른 글

키워드 정리 [11] - OOP  (1) 2023.12.01
키워드 정리 [10] - Template  (0) 2023.12.01
키워드 정리 [8] - 정수, 실수  (1) 2023.12.01
키워드 정리 [7] - 컴파일  (1) 2023.12.01
키워드 정리 [6] - 함수 호출 규약  (0) 2023.12.01

+ Recent posts