[STL] 함수 포인터

치치·2025년 2월 12일

STL

목록 보기
4/21
post-thumbnail

함수 포인터를 알기 전에 포인터에 대해 알고가자

함수 포인터

  • 포인터 : 주소를 저장하는 메모리 공간 이름

  • 함수 포인터 : 함수의 시작주소를 저장하는 포인터
    -> 함수의 이름은 함수가 시작하는 시작 주소
    -> ex)

void Print (int n) { } // 함수
 
void (*pf) (int); // 함수 포인터 
pf = Print; // 함수 포인터에 함수의 시작주소 저장

함수 포인터 : 반환형타입 (*함수포인터 이름) (매개변수)



✅정적 함수 포인터

  • 클래스 객체와 관계없이 호출이 가능한 포인터이다
    -> 어떤 객체를 가리킬 지 신경 쓸 필요가 없다

  • 정적 함수 포인터는 주로 전역함수, 클래스 내부 static 멤버 함수 호출에 사용된다

근데 정적 멤버 함수 자체는 정적 함수 포인터를 사용하지 않더라도 객체없이 바로 접근이 가능함

-> 굳이 사용하지 않아도 되지만, 포인터 배열을 사용하거나 런타임 중 동적으로 정적 멤버 함수를 선택하고 싶을땐 사용


정적 함수 포인터 사용 예제

  1. 전역함수 Print(int n)와 멤버함수 Print(int n)를 생성

  2. 메인함수에 정적 함수 포인터 선언 void (*ptr)(int)
    -> 반환형(void) (*포인터 이름) (매개변수 타입&갯수)

#include <iostream>
using namespace std;

void Print(int n)
{
	cout << "전역 함수 : " << n << endl;
}

class Point
{
public:
	static void Print(int n)
	{
		cout << "Point 클래스의 정적 멤버 변수 : " << n << endl;
	}
};

int main()
{
	void (*ptr)(int); // 정적 함수 포인터 선언

	ptr = Print; // 포인터에 함수 이름(주소) 저장

	ptr(10); // 전역 함수 호출


	ptr = Point::Print;

	ptr(10); // 정적 멤버 함수 호출

}
  • 포인터 변수 이름에 함수의 이름(주소)를 저장하면 해당 주소의 함수를 호출할 수 있다! (매개변수도 당연히 넣어서)


정적함수 (static)

  • 보통이라면 멤버함수를 호출할땐, 클래스의 객체를 생성해서 객체를 통해 함수에 접근하여 호출하는데
    -> 정적함수는 객체 생성없이 바로 접근이 가능하다!

ex) MyClass 타입의 객체를 생성하지 않아도 MyClass:: 함수 이름으로 호출이 가능하다



✅멤버 함수 포인터

  • 객체의 상태에 접근해야한다
    -> '어떤 객체의' '어떤 멤버 함수'를 가리킬 것인지 명시해야한다

반환형타입 (호출할 클래스::*함수포인터 이름) (매개변수)

  • 멤버 함수 호출 방법에 따라 2가지로 나뉜다
    -> 1. 객체로 호출 2. 주소로 호출

멤버 함수 포인터 사용 예제

-> 멤버 함수 포인터 선언 자체는 동일하게 하지만, 함수의 호출방법은 서로 다르다

1. 객체로 멤버 함수 호출

  1. MyClass클래스 안의 멤버 함수인 memberFunction 함수를 호출하고 싶다

  2. 멤버 함수 포인터 생성
    -> void (MyClass:: * ptr)();

  3. 포인터에 내가 호출하고자 하는 멤버함수의 주소값을 저장한다
    -> ptr = &MyClass::memberFunction;

포인터 생성하고 값을 초기화 하는 과정을 한번에 적어도 상관없다 (나는 가독성을 위해 따로 적었다)
-> void (MyClass::*ptr)() = &MyClass::memberFunction;

  1. 객체를 이용하여 함수를 호출할 것이기 때문에, 해당 클래스의 객체를 만들고
    (객체.*포인터이름)(매개변수); 로 호출
    -> (obj.*ptr)();
#include <iostream>
using namespace std;

class MyClass
{
public: 
	void memberFunction()
	{
		cout << "멤버함수 호출(콜)! " << endl;
	}
};

int main()
{
	MyClass obj;

	void (MyClass:: * ptr)();

	ptr = &MyClass::memberFunction;

	(obj.*ptr)();
}

2. 주소로 멤버 함수 호출

  1. 객체를 통한 호출과 동일하게 멤버 함수 포인터를 생성하고 해당 멤버 함수의 주소값을 저장한다
    void (MyClass:: * ptr)();
    ptr = &MyClass::memberFunction;

-> ptr은 MyClass 클래스의void memberFunction()을 가리킬 수 있는 포인터

  1. 주소로 멤버 함수를 호출하는 방법은 포인터를 사용한 것이다
    -> MyClass 타입의 객체 obj를 만들고, 이 객체의 주소값을 가리킬 *p를 생성한다

  2. *p는 MyClass 객체의 주소값을 가리키고 있기 때문에, p를 이용하여 멤버 함수 포인터에 접근하여 함수 호출이 가능하다

-> (p -> *ptr)();

-> p->*ptr은 p 포인터가 가리키는 객체에서 ptr이 가리키는 멤버 함수를 호출하는 방식

근데 내가 보기엔 객체를 사용하여 바로 포인터를 호출하는 게 더 효율적인 거 같다
-> 굳이 객체의 주소를 가리키는 포인터를 만들고 그 주소에서 ptr이 가리키는 함수를 호출하는 건 두번 일하는 거 아닐까 싶다 (심지어 성능차이도 없음)

#include <iostream>
using namespace std;

class MyClass
{
public: 
	void memberFunction()
	{
		cout << "멤버함수 호출(콜)! " << endl;
	}
};

int main()
{
	MyClass obj;

	MyClass *p = &obj;

	void (MyClass:: * ptr)();

	ptr = &MyClass::memberFunction;

	(p->*ptr)();
}


포인터를 사용하지 않고 그냥 호출하기

  1. 정적 함수 포인터를 사용하지 않고 함수 호출
  • 위에서 봤지만, 정적 함수 포인터로 호출할 수 있는 함수들은 주로 전역함수, 정적 멤버 함수, namespace안의 전역함수 등이 있다
  • 이 함수들은 포인터를 사용하지 않아도 그냥 호출할 수 있다
    -> 예를 들면, static으로 생성된 정적 멤버 함수는 클래스의 객체를 생성하지 않고도 함수 호출이 가능하다 (MyClass::memberFunction();)

  1. 멤버 함수 포인터를 사용하지 않고 함수 호출
  • 멤버 함수 포인터는 말그대로 내가 원하는 클래스 안의 멤버 함수를 호출하기 위해서 사용되는 포인터인데, 굳이 포인터를 사용하지 않아도 객체를 생성하고 바로 호출이 가능하다
    -> MyClass클래스의 obj객체를 생성하고, 객체를 사용하여 멤버 함수(memberFunction)를 호출함


그럼 왜 함수 포인터를 사용할까?

  • 동적으로 함수 호출이 가능하다

    1. 런타임(실행) 중에 어떤 멤버 함수가 호출될 지 결정할 수 있는 것!
    2. 즉, 실행도중에 포인터의 값을 변경하면서 어떤 멤버 함수를 호출할 지 결정하는 방식이다
  • 어떤 조건에 따라 다르게 처리하고 싶은 경우에 사용한다

ex) 멤버 함수 포인터 사용

#include <iostream>
using namespace std;

class MyClass
{
public:
	void FunctionA()
	{
		cout << "멤버함수 A호출(콜)! " << endl;
	}
	void FunctionB()
	{
		cout << "멤버함수 B호출(콜)! " << endl;
	}
};

int main()
{
	MyClass obj;

	void (MyClass:: * ptr)();

	ptr = &MyClass::FunctionA;
	(obj.*ptr)();

	ptr = &MyClass::FunctionB;
	(obj.*ptr)();

	return 0;
}
  1. 멤버함수 FunctionA와 FunctionB가 있는데 프로그램을 실행하면(런타임), 멤버 함수 포인터에 동적으로 주소값이 저장된다
    -> 프로그램 시작 전까지는 멤버 함수 포인터가 선언만 된 상태 (주소값 할당 전)

  2. 여러개의 멤버 함수가 있고, 런타임 중에 결정되야 한다면 멤버 함수 포인터를 사용하는 게 좋겠죠


  • 콜백함수 구현에도 사용된다
    -> 다른 글에서 따로 설명하겠당
profile
뉴비 개발자

0개의 댓글