일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- conversion constructor
- std::cout
- this call
- member function pointer
- constructor
- base from member
- operator overloading
- placement new
- dynamic_cast
- vector capacity
- C++
- c++ multi chatting room
- 더 지니어스 양면포커
- c++ basic practice
- virtual inheritance
- std::vector
- increment operator
- virtual function
- return by reference
- virtual function table
- new&delete
- suffix return type
- diamond inheritance
- pointer to member data
- std::endl
- virtual destructor
- std::ostream
- delete function
- discord bot
- vector size
- Today
- Total
I'm FanJae.
[C++ Intermediate] Member Function Pointer 본문
1. Member Function Pointer
1-1. 함수 포인터와 멤버 함수
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
void(X::*f2)(int) = &X::mf1; // ok
}
- 일반 함수 포인터에 멤버 함수의 주소를 담을 수 없다.
- 일반 함수 포인터에 static 멤버 함수는 담을 수 있다.
※ 일반 함수 포인터와 멤버 함수 포인터
void(*f1)(int) = &foo;
void(X::*f2)(int) = &X::mf1;
※ 함수의 주소를 얻는 방법
foo | ok. 함수 이름은 함수 주소로 암시적 변환 가능 |
&foo | ok. |
X::mf1 | error. 멤버함수는 반드시 주소 연산자(&) 필요 |
&X::mf1 | ok. |
1-2. pointer to 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
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); // .*을 pointer to member 연산자라고 한다.
// 다만 괄호 연산자의 우선순위가 높기 때문에 f2(10)을 먼저 한다.
// 따라서 이것도 연산자 우선순위 문제로 에러가 발생한다.
(obj.*f2)(10); // 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를 가리키는 포인터 |
1-3. 일반 함수 포인터와 멤버 함수 포인터의 차이
#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); // 멤버 함수 포인터 사용
}
- 함수 포인터를 사용해서 함수를 호출하는 방법이 다르다.
- STL 에서는 몇가지 도구가 제공된다.
※ std::invoke ( C++17 )
- funtional 헤더
- 일반 함수 포인터와 멤버 함수 포인터(정확히는 callable object)를 동일한 방법으로 사용(호출)할 수 있다.
std::invoke(일반 함수 포인터, 함수 인자들...); | |
std::invoke(멤버 함수 포인터, 객체, 함수 인자들...); std::invoke(멤버 함수 포인터, &객체, 함수 인자들...); |
#include <functional>
class X
{
public:
void mf1(int a) {}
};
void foo(int a) {}
int main()
{
std::invoke(f1, 10); // f1(10)
std::invoke(f2, obj, 10);
std::invoke(f2, &obj, 10);
auto f3 = std::mem_fn(&X::mf1);
f3(obj, 10);
f3(&obj, 10);
}
※ std::invoke ( C++11 )
- functional 헤더
- 멤버 함수의 주소를 인자로 받아서 함수 주소를 담은 래퍼 객체 반환.
void(X::*f2)(int) = &X::mf1; (obj.*f2)(10) |
auto f3 = std::mem_fn(&X::mf1); f3(obj, 10); f3(&obj, 10); |
2. Pointer to Member data
#include <iostream>
struct Point
{
int x;
int y;
};
int main()
{
int num = 0;
int* p1 = #
int Point::*p2 = &Point::y;
}
- 멤버 데이터를 가리키는 포인터이다.
- 이 예제에서 다음과 같은 생각을 해볼 수 있다. p1은 num 변수의 메모리 주소를 가지고 있다.
- p2는 무엇을 담을까?
※ 대부분의 컴파일러에서 p2는 Point 구조체 안에서 y의 offset(4)을 담고 있다. - 공식 표준은 아니며, 컴파일러 마다 이는 다르다.
#include <iostream>
struct Point
{
int x;
int y;
};
int main()
{
int num = 0;
int* p1 = #
int Point::*p2 = &Point::y; // 몇번째 offset인지 (정확히는 아님)
*p1 = 10; // ok
Point pt;
pt.*p2 = 10; // pt.y = 10
// *((char *)&pt + p2) = 10;
std::cout << p2 << std::endl; // 1
printf("%p\n",p2); // 4
}
#include <iostream>
#include <functional>
struct Point
{
int x;
int y;
};
int main()
{
int Point::*p = &point::y;
Point obj;
obj.*p = 10; // obj.y = 10
(&obj)->*p = 10;
std::invoke(p, obj) = 20; // obj.y = 20
int n = std::invoke(p, obj);
std::cout << n << std::endl;
std::cout << obj.y << std::endl;
}
※ 멤버 데이터를 가리키는 포인터(p)를 사용해서 객체의 멤버 데이터에 접근하는 2가지 방법
- 객체 obj 생성을 한 이후 다음과 같이 사용 가능하다.
.*, ->* 연산자 (pointer to member) |
obj.*p = 10; (&obj)->*p = 10; |
std::invoke | std::invoke(p, obj) = 10; int n = std::invoke(p, obj); |
- invoke에서 첫번째 인자에 들어가는 값은 멤버 함수의 포인터 뿐 아니라, 멤버 데이터의 포인터도 넣을 수 있다.
※ callable type
- std::invoke()를 사용할 수 있는 타입
- 함수, 함수 포인터, 멤버 함수 포인터, 멤버 데이터 포인터, 람다 표현식이 만드는 타입, operator() 함수를 제공하는 타입
'C++ > Intermediate' 카테고리의 다른 글
[C++ Intermediate] Max Implement (0) | 2024.09.27 |
---|---|
[C++ Intermediate] this call (1) | 2024.09.22 |
[C++ Intermediate] Trivial (4) | 2024.09.19 |
[C++ Intermediate] new/delete, placement new (0) | 2024.09.18 |
[C++ Intermediate] 객체의 변환(Conversion) (0) | 2024.09.13 |