this_call 1. this 객체가 여러번 인스턴스화 한다고 해서 멤버 함수가 생성되는게 아니라 인자가 추가 되는 개념으로 바뀐다.
1. 멤버 함수의 호출 원리 객체가 함수의 1번째 인자(this)로 추가된다. - this call 정확히는 ecx 레지스터로 전달
2. static 멤버 함수는 this가 추가되지 않는다 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 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> using namespace std;class Point { int x, y; public : void set (int a, int b) { x = a; y = b; } static void foo (int a) { x = a; } }; int main ( ) { Point::foo (10 ); Point p1, p2; p1.set (10 , 20 ); }
2. 함수포인터 멤버 함수의 포인터를 만드는 법
1. 일반 함수 포인터에 멤버 함수의 주소를 담을 수 없다. this때문에..!!
2. 일반 함수 포인터에 static 멤버 함수의 주소를 담을 수 있다. 3. 멤버 함수 포인터를 만들고 사용하는 방법 1 2 3 void (Dialog::*f3)() = &Dialog::Close;Dialog dlg; (dlg.*f3)();
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 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> using namespace std;class Dialog {public : void Close () { cout << "Dialog Close" << endl; } }; void foo () { cout << "foo" << endl; } int main () { void (*f1)() = &foo; void (Dialog::*f3)() = &Dialog::Close; Dialog dlg; (dlg.*f3)(); Dialog *pDlg = &dlg; ((*pDlg).*f3)(); (pDlg->*f3)(); return 0 ; }
3. this 관리의 어려움 일반적은 쓰레드 관리를 통해서 this 관리의 어려움을 본다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> #include <windows.h> #include <conio.h> using namespace std;DWORD __stdcall foo (void * p) { cout << "foo" << endl; return 0 ; } int main () { CreateThread (0 , 0 , foo, "A" , 0 , 0 ); _getch(); return 0 ; }
4. this 관리의 어려움 일반적은 쓰레드 관리를 통해서 this 관리의 어려움을 본다
핵심 1. C의 callback 함수는 객체 지향으로 디자인 될때 static 멤버함수가 되어야 한다. 핵심 2. static 멤버에는 this가 없으므로 가상함수나 멤버 data에 접근할 수 없다. 다양한 기법으로 this를 사용할 수 있게 하는것이 편리하다. 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> #include <windows.h> #include <conio.h> using namespace std;class Thread {public : void Create () { CreateThread (0 , 0 , _threadMain, this , 0 , 0 ); } static DWORD __stdcall _threadMain(void *p) { threadMain (); Thread* self = static_cast <Thread*>(p); self->threadMain (); return 0 ; } virtual void threadMain () { } }; class MyHtread : public Thread {public : virtual void threadMain () { cout << "MyThread" << endl; } }; int main () { MyHtread t; t.Create (); return 0 ; }
5. NULL 객체 호출 문제 NULL 객체에 대해서 handling을 해준다. NULL 객체에서 호출하는 문제는 this를 생각하면 쉽게 풀어 나갈 수 있다.
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 31 32 33 34 35 36 37 38 39 40 41 #include <iostream> using namespace std;class Test { int data; public : void f1 () { cout << "f1" << endl; } int f2 () { cout << "f2" << endl; return 0 ; } int f3 () { cout << "f3" << endl; return data; } int call_f3 () { return this ? f3 () : 0 ; } virtual void f4 () { }; }; int main () { Test *p = 0 ; p->f1 (); p->f2 (); p->f3 (); p->call_f3 (); p->f4 (); }
6. 상속과 포인터 상속 순서에 따라서 포인터 주소를 배정 받는다. 다운케스팅을 하면 자동으로 그 주소를 찾아 간다. 부모의 주소를 100번지라고 가정을 하면 상속 받은 순서에 따라서 주소를 배정을 받게 된다.
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 31 32 #include <iostream> using namespace std;class X {public : int x; }; class Y {public : int y; }; class C : public X, public Y {public : int c; }; int main () { C c; cout << &c << endl; X *pX = &c; Y *pY = &c; cout << pX << endl; cout << pY << endl; return 0 ; }
6. 상속과 포인터 우리가 예상하는 것보다 컴파일러는 주소 값을 잘 찾아서 간다. 컴파일러가 어떻게 이 과정을 찾는지는 모른다.
모든 함수 포인터는 4바이트라고 생각했지만 일반적은 함수 포인터만 4바이트 다중 상속을 하는 경우 포인터의 주소는 8바이트 인다. { 함수주소, this offset }
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 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <iostream> using namespace std;class X {public : int x; void fx () { cout << this << endl; } }; class Y {public : int y; void fy () { cout << this << endl; } }; class C : public X, public Y {public : int c; }; int main () { C c; cout << &c << endl; c.fx (); c.fy (); void (C::*f)(); f = &C::fy; (c.*f)(); cout << sizeof (f) << endl; return 0 ; }
7. 함수포인터와 가상함수 가상함수의 경우는 가상함수 테이블의 인덱스 번호가 넘어온다. 즉, 가상함수 table의 인덱스, 가상함수의 순서가 나오게 된다. g++ : 0, 1, 2, 3 등의 숫자가 나오게 됩니다. vc++ : 주소 비슷하게 나오는데… 그 주소를 따라가면 index가 있습니다.
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 #include <iostream> using namespace std;class Base {public : virtual void goo () { cout << "Base foo" << endl; } virtual void foo () = 0 ; }; class Dervied : public Base {public : virtual void foo () { cout << "Derived foo" << endl; } }; int main () { void (Base::*f)() = &Base::foo; printf ("%d\n" , f); Base *p = new Dervied; (p->*f)(); }
8. Handler 하나의 클래스를 이용해서 각각의 인스턴스가 다른 역활을 하기 위해서는 Handler를 이용해서 처리한다. java에서 리스너와 비슷한 처리 방법이다. c/c++에서는 모든 함수의 주소를 담을 수 있는 도구가 없다.
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 #include <iostream> using namespace std;template <class T >class Button { void (*handler)(); public : void setHandler (void (*f)()) { handler = f; } void click () { handler (); } }; void Btn1Handler () { cout << "버튼 1 클릭" << endl; }int main () { Button b1; b1.setHandler (&Btn1Handler); b1.click (); return 0 ; }
다음과 같이 처리 하면 모든 클래스에 대해서 처리를 할 수 가 없다. 이럴 때 사용하는 것이 template 이다.
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 31 class Dialog {public : void Close () { cout << "Dialog Close" << endl; } }; template <typename T>class Button { void (T::*handler)(); T *member; public : void setHandler (void (T::*f)()) { handler = f; } void click () { (member->*handler)(); } }; int main () { Button<Dialog> b1; b1.setHandler (&Dialog::Close); b1.click (); }
다음과 같이 코드를 수정하면 모든 클래스에 대해서 처리를 할 수가 있다.
9. function<> 모든 함수의 주소를 담을 수 있는 도구 c,c++ : 문법적으로는 없다. c# : delegate 라는 문법 objective-c : Selector 라는 문법
c++11 : function<> 모든 함수의 주소를 담을 수 있다.
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 31 32 33 #include <iostream> #include <functional> using namespace std;void foo () { cout << "foo" << endl; }void goo (int a) { cout << "goo : " << a << endl; }void hoo (int a, int b) { cout << "hoo : " << a << ", " << b << endl; }class Dialog {public : void Close () { cout << "Dialog Close" << endl; } }; int main () { function<void ()> f = &foo; f (); f = bind (&goo, 5 ); f (); f = bind (&hoo, 1 , 2 ); f (); Dialog dlg; f = bind (&Dialog::Close, dlg); f (); return 0 ; }
10. Bind bind에 사용법을 더 자세하게 배워보자
namespace std::placeholders; ``` _1, _2, .... 이용해서 입력하는 인자의 위치를 정해 준다. 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 31 32 33 34 35 36 37 38 ```c++ #include <iostream> #include <functional> using namespace std; using namespace std::placeholders; void foo() { cout << "foo" << endl; } void goo(int a) { cout << "goo : " << a << endl; } void hoo(int a, int b) { cout << "hoo : " << a << ", " << b << endl; } class Dialog { public: void Close() { cout << "Dialog Close" << endl; } }; void koo(int a, int b, int c, int d) { printf("%d %d %d %d\n", a, b, c, d); } int main() { function<void(int)> f = &goo; f(5); //goo(5) f = bind(&hoo, _1, 3); f(5); //hoo(5, 3) function<void(int, int)> f2; f2 = bind(&koo, _2, 2, 9, _1); f2(6, 3); // 3, 2, 9, 6 return 0; }
Hoyuo 안드로이드 개발자입니다