«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
04-24 07:56
관리 메뉴

lancelot.com

pointer to member function 본문

프로그래밍

pointer to member function

lancelot50 2022. 8. 8. 00:09
  • 멤버함수의 호출원리(this call)

class Point
{
public:
	int x{ 0 };
	int y{0};
public :
	void set(int a, int b) { x = a; y = b; }
};

int main()
{
	Point pt1;
	Point pt2;
	
	pt1.set(10, 20);
	pt2.set(10, 20);
}

컴파일러가 변경한 코드

  • 주의
    • 실제 함수 인자가 전달되는 방식과 객체 주소가 전달되는 방식은 약간의 차이가 있다.
    • 표준으로 정해진 사항은 아니므로 32bit/ 64bit 환경, 컴파일러에 따라서도 차이가 있을 수 있음.
  •  static member 함수는 객체의 주소가 전달되지 않는다.  this call 이 아님.
  • 객체의 주소를 알 수 없기때문에 x, y에 접근할 수 없음.

 

  • member function pointer
    • 일반 함수 포인터에 "멤버 함수의 주소를 담을 수 없다"
    • 일반 함수 포인터에 "static member 함수는 담을 수 있다"
class X
{
public :
		void mf1(int a) {}	// void mf1(X* this, int a)
	static	void mf2(int a) {}
};

void foo(int a) {}

int main()
{
	void (*f1)(int) = &foo;		// ok	 
//	void (*f2)(int) = &X::mf1;	// error 일반 함수 포인터에 멤버함수 주소를 담을 수 없다.
	void (*f3)(int) = &X::mf2;	// ok	 일반 함수 포인터에 static 멤버함수는 담을 수 있다.

	void(X:: * f2)(int) = &X::mf1;	// ok

	f1(10);	// ok. 일반 함수포인터로 함수 호출.
//	f2(10);	// error. 객체가 필요하다.

	X obj;
//	obj.f2(10);	// error. f2라는 멤버를 찾게된다

	//pointer to member 연산자 사용
//	obj.*f2(10);	// error 연산자 우선순위 문제
	(obj.*f2)(10);	// ok
}

멤버함수 포인터

  • 함수의 주소를 얻는 방법
    • foo : ok. 함수 이름은 함수 주소로 암시적 변환 가능
    • &foo : ok
    • X::mf1 : error. 멤버함수는 반드시 주소연산자(&) 필요
    • &X::mf1 : ok
  •  멤버함수 포인터를 사용해서 멤버 함수를 호출하는 방법
    • f2(10) : error. 멤버함수를 호출하려면 객체가 필요
    • obj.f2(10) : error. obj 객체에서 f2라는 멤버가 있는지 검색
    • obj.*f2(10) : error. () 연산자가 .* 연산자보다 우선순위가 높다
    • (obj.*f2)(10) : ok
  • Pointer to Member Operator
    • 멤버에 대한 포인터(멤버함수 포인터, 멤버 변수 포인터)를 사용해서 멤버에 접근할 때 사용하는 연산자
    • .* : (obj.*f2)(10);
    • ->* : (pObj->*f2)(10);  pobj는 obj를 가리키는 포인터

 

  • 일반 함수포인터와 멤버함수 포인터
    • 함수포인터를 사용해서 함수를 호출하는 방법이 달라서 불편할 경우가 있다.
    • STL에는 이것을 도와주는 몇가지 도구가 제공됨.
  •  std::invoke ( C++17 )
    • <functional> header
    • 일반 함수포인터와 멤버 함수포인터(정확히는 callable object)를 동일한 방법으로 사용(호출)할 수 있다.

std::invoke - C++17

  • std::mem_fn ( C++ 11 )
    • <functional> header
    • 멤버 함수의 주소를 인자로 받아서 함수 주소를 담은 래퍼 객체 반환.

std::mem_fn - C++11

#include<functional>

class X
{
public :
			void mf1(int a) {}
};

void foo(int a) {}

int main()
{
	X obj;
	void (*f1)(int) = &foo;		
	void(X::*f2)(int) = &X::mf1;

	f1(10);			// 일반 함수포인터 사용
	(obj.*f2)(10);	// 멤버 함수포인터 사용

//	f2(&obj, 10);	// 이렇게 사용가능하면 좋지 않을까?
					// uniform call syntax 제안
					// 채택 안됨
	// std::invoke(C++17)
	//  -> <funtional> header
	//  -> 일반 함수포인터와 멤버 함수포인터(정확히는 callable object)를 동일한 방법으로 사용(호출) 할 수 있다.
	std::invoke(f1, 10);		// f1(10);
	std::invoke(f2, obj, 10);
	std::invoke(f2, &obj, 10);

	// std::mem_fn(C++11)
	//  -> <funtional> header
	//  -> 멤버 함수의 주소를 인자로 받아서 함수 주소를 담은 래퍼 객체 반환.
	auto f3 = std::mem_fn(&X::mf1);
	f3(obj, 10);
	f3(&obj, 10);  
}