this call
- 멤버함수의 호출 원리
- 객체가 함수의 1번째 인자(this) 로 추가 된다. - this call
- 정확히는 exc 레지스터로 전달
-
static 멤버 함수는 this 가 추가되지 않는다. ```cpp class Point { int x, y; public: void set(int a, int b) { // void set(Point* const this, int a, int b) x = a; // this->x = a; y = b; // this->y = b; }
static void foo(int a) { // void foo(int a) x = a; // error C2597: 비정적 멤버 ‘Point::x’에 대한 참조가 잘못되었습니다. // this->x = a 가 되어야 하는데 this 가 없다. } // 그래서 static 멤버에서는 멤버변수 접근이 안된다. };
int main() { Point::foo(10); // static 멤버함수는 객체없이 호출 가능 // push 10 // 보낼 객체가 없다. // call Point::foo
Point p1, p2;
p1.set(10, 20); // set(&p1, 10, 20) 이 된다.
// push 20
// push 10 매개변수는 스택으로
// mov ecx, &p1 객체 주소는 레지스터에
// call Point::set } ``` >error code >- [C2597 비정적 멤버 참조는 특정 개체에 상대적이어야 합니다.](https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-errors-2/compiler-error-c2597){:target="_blank"} --- 1. 일반 함수 포인터에 멤버함수의 주소를 담을 수 없다. this 때문에. 2. 일반 함수 포인터에 static 멤버함수의 주소를 담을 수 있다. ```cpp #include<iostream>
class Dialog { public: void Close() { std::cout « “Dialog close” « std::endl; } };
void foo() { std::cout « “foo” « std::endl; }
int main() { void(f1)() = &foo; // ok. void(f2)() = &Dialog::Close; // error C2440: ‘초기화 중’: ‘void (__cdecl Dialog::* )(void)’에서 ‘void (__cdecl *)(void)’(으)로 변환할 수 없습니다.
void(Dialog:: * f3)() = &Dialog::Close; // ok. 멤버함수 포인터를 만드는 법
f3(); // error C2064: 항은 0개의 인수를 받아들이는 함수로 계산되지 않습니다.
// this 가 없다.
Dialog dlg;
dlg.f3(); // error C2039: 'f3': 'Dialog'의 멤버가 아닙니다.
// dlg.Close() 즉 f3(&dlg)
// 하지만 이 경우 컴파일러가 f3 이라는 멤버를 찾게되어 error
(dlg.*f3)(); // f3 은 함수포인터 이므로 *f3 하면 함수가 된다.
// .*연산자 우선 순위를 함수호출() 보다 높여야 한다.
Dialog* pDlg = &dlg;
(pDlg->*f3)(); // 포인터인 경우 arrow operator 로 호출할 수 있다. } ``` >error code >- [C2440 "void(Dialog::*)()"형식의 값을 사용하여 "void(*)()"형식의 엔터티를 초기화할 수 없습니다.](https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-errors-1/compiler-error-c2440){:target="_blank"} >- [C2064 명백한 호출의 괄호 앞에 오는 식에는 함수 (포인터) 형식이 있어야 합니다.](https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-errors-1/compiler-error-c2064){:target="_blank"} >- [C2039 클래스 "Dialog"에 "f3" 멤버가 없습니다.](https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-errors-1/compiler-error-c2039) --- this 관리의 어려움 ```cpp #include <iostream> #include <windows.h> #include <conio.h>
DWORD __stdcall foo(void* p) { std::cout « “foo” « std::endl; return 0; }
int main() { CreateThread(0, 0, foo, “A”, 0, 0); // 스레드 생성
_getch(); } ``` C 의 스레드를 C++ 로 캡슐화 해 보자 ```cpp #include <iostream> #include <windows.h> #include <conio.h>
class Thread { // 클래스 라이브러리 내부 클래스라고 가정 public: void Create() { CreateThread(0, 0, _threadMain, this, 0, 0); // 스레드 함수 // 1. C 의 callback 함수는 객체지향으로 디자인 될때 // static 멤버함수가 되어야 한다. // 2. static 멤버에는 this 가 없으므로 // 가상함수나 멤버 data에 접근할 수 없다. // 다양한 기법으로 this 를 사용할수 있게 // 하는것이 편리하다. }
static DWORD __stdcall _threadMain(void* p) {
Thread* self = static_cast<Thread*>(p); // p 가 this 이므로 캐스팅하여 사용
self->threadMain(); // 결국 threadMain(self);
return 0;
}
virtual void threadMain() {} // threadMain(Thread *this); };
class MyThread : public Thread { // 라이브러리 사용자 클래스 public: virtual void threadMain() { std::cout « “My Thread” « std::endl; } };
int main() { MyThread t; t.Create(); // 이 순간 스레드가 생성되어 // threadMain 을 수행해야 한다. _getch(); }
---
NULL 객체의 함수 호출 문제
```cpp
#include <iostream>
class Test {
int data;
public:
void f1() {
std::cout << "f1" << std::endl;
}
int f2() {
std::cout << "f2" << std::endl;
return 0;
}
int f3() {
std::cout << "f3" << std::endl;
return data; // this->data;
}
int call_f3() {
return this ? f3() : 0; // NULL 객체에 대해 함수를 호출 해도 죽지않게 하기 위한 함수
}
virtual void f4() {}
};
int main() {
Test* p = nullptr; // 메모리 할당에 실패해 nullptr 이 나왔다고 가정
p->f1(); // f1(p), f1(nullptr) : 어떻게 될까?
p->f2(); // ok.
p->f3(); // error. this이(가) nullptr였습니다.
p->call_f3(); // ok.
p->f4(); // nullptr 번지의 가상함수 테이블을 찾으려 한다.
// runtime error.
}
다중상속과 this, 그리고 함수 포인터
#include <iostream>
class X {
public:
int x;
void fx() {
std::cout << this << std::endl;
}
};
class Y {
public: int y;
void fy() {
std::cout << this << std::endl;
}
};
class C : public X, public Y {
public: int c;
};
int main() {
C ccc;
std::cout << &ccc << std::endl; // 100번지라고 할때,
X* pX = &ccc;
Y* pY = &ccc;
std::cout << pX << std::endl; // 100번지
std::cout << pY << std::endl; // 104번지
ccc.fx(); // 100번지
ccc.fy(); // 104번지
void(C:: * f)(); // 16Byte { 함수 주소, this offset }
f = &C::fx; // f = { fx 주소, 0 }
(ccc.*f)(); // f(&ccc), 100번지
f = &C::fy; // f = { fy 주소, sizeof(x) 즉 4 }
(ccc.*f)(); // f(&ccc), 104번지
// f.함수주소(&ccc + f.this_offset)
std::cout << sizeof(f) << std::endl;
}
가상함수의 주소를 꺼내면 진짜 주소가 아닌 가상함수 테이블의 index, 즉 가상함수의 순서가 나오게 된다.
- g++, xCode : 0, 1, 2, 3 등의 숫자
- VC++ : 주소비슷하게 나오는데 그 주소를 따라가면 index 가 나온다.
```cpp
#include
class Base { public: virtual void goo() { std::cout « “Base goo” « std::endl; } virtual void foo() { std::cout « “Base foo” « std::endl; } virtual void hoo() { std::cout « “Base hoo” « std::endl; } };
class Derived : public Base { public: virtual void foo() { std::cout « “Derived foo” « std::endl; } };
int main() { void (Base:: * f1)() = &Base::goo; void (Base:: * f2)() = &Base::foo; void (Base:: * f3)() = &Base::hoo;
printf("%d\n", &Base::goo); // goo 의 index
printf("%d\n", &Base::foo); // foo 의 index
printf("%d\n", &Base::hoo); // hoo 의 index
Base* p = new Derived;
(p->*f1)(); // p->goo(), goo(&p) } ``` --- callback 과 함수 포인터 문제 ```cpp #include <iostream>
class Button { void(T::* handler)(); Dialog* pDlg;
public: void setHandler(void(*f)()) { handler = f; }
void click() {
handler(); // 버튼이 눌렸다는 사실을 외부에 전달 한다.
// 흔히 "객체가 외부에 이벤트를 발생한다" 라고 표현
} };
void btn1Handler() { std::cout « “버튼 1 클릭” « std::endl; }
int main() { Button b1, b2; b1.setHandler(&btn1Handler); // 버튼에 callback 함수 등록 b1.setHandler(&Dialog::Close); // ?? b1.click(); // 사용자가 버튼을 클릭하면 이 함수가 호출된다고 가정. }
---
모든 함수의 주소를 담을 수 있는 도구
- C, C++ : 문법적으로는 없다.
- C# : delegate 라는 문법
- Objective-C : selector 라는 문법
- C++11 : function<> 모든 함수의 주소를 담을 수 있다.
```cpp
#include <iostream>
void foo() {
std::cout << "foo" << std::endl;
};
void goo(int a) {
std::cout << "goo : " << a << std::endl;
};
void hoo(int a, int b) {
std::cout << "hoo : " << a << " " << b << std::endl;
};
class Dialog {
public:
void Close() {
std::cout << "Dialog close" << std::endl;
}
};
#include <functional>
void koo(int a, int b, int c, int d) {
std::cout << a << " " << b << " " << c << " " << d << std::endl;
}
int main() {
std::function<void()> f = &foo;
f(); // ok... foo() 호출
f = std::bind(&goo, 5); // 인자 고정
f(); // goo(5);
Dialog dlg;
f = std::bind(&Dialog::Close, &dlg); // 객체를 고정
f = std::bind(&hoo, 1, 2);
f(); // hoo(1, 2);
std::function<void(int)> f1 = &goo;
f1 = &goo;
f1(5); // goo(5);
f1 = bind(&hoo, std::placeholders::_1, 3);
f1(5); // hoo(5, 3);
std::function<void(int, int)> f2;
f2 = bind(&koo, std::placeholders::_2, 2, 9, std::placeholders::_1);
f2(6, 3); // koo(3, 2, 9, 6);
// std::placeholders::_1, _2, _3 ...
// placeholder 로 인자를 전달 할 수 있다.
}
댓글남기기