본문 바로가기

프로그래밍 언어 노트/C++ | Modern C++

[C++] 숨겨진 This pointer 는 어떻게 넘어 가는가~

파이썬의 경우 self (C++ 의 this) 를 명시적으로 써준다.

그리고 객체.함수(파라미터1, 파라미터2) 에서 묵시적으로 객체 자신을 첫 파라미터로 넘기게 되는데 (바운드)

따라서 클래스명.함수(객체, 파라미터1, 파라미터2) 와 동일하다. (언바운드)

 

1
2
3
4
5
6
7
class A(object):
    def fun(self):
        print("fun")
 
= A()
a.fun()
A.fun(a)
cs

 

그런데 C++ 에서는 this 포인터를 묵시적으로 넘긴다고 배웠는데 (숨겨진 this 포인터)

그럼 파이썬마냥 A::fun(*a) 와 같이 호출 할 수 있어야 하는것 아닌가?

근데 안된다. GCC는 모르겠는데 일단 ViusalC++ 에서는 안된다.

뭐 안되는건 안되는거고 실제로 This pointer가 파라미터로 넘어가긴 하는지 확인해보기 위하여

간단한 코드를 제작하고 디스어셈블리창을 통해 확인해보았다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;
 
class A
{
public:
    void  Fun(intint) {}
    static void Fun(A* a, intint) {};
};
 
void  Fun(A* a, intint) {}
 
int main()
{
    A a;
 
    cout << "Reg" << endl;
    Fun(&a, 12);
    a.Fun(12);
    A::Fun(&a, 12);
    return 0;
}
cs

 

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    //Fun(&a, 1, 2);
011F293B  push        2  
011F293D  push        1  
011F293F  lea         eax,[a]  
011F2942  push        eax  
011F2943  call        Fun (011F16AEh)  
011F2948  add         esp,0Ch  
    //a.Fun(1, 2);
011F294B  push        2  
011F294D  push        1  
011F294F  lea         ecx,[a]  
011F2952  call        A::Fun (011F16B3h)  
    //A::Fun(&a, 1, 2);
011F2957  push        2  
011F2959  push        1  
011F295B  lea         eax,[a]  
011F295E  push        eax  
011F295F  call        A::Fun (011F16A9h)  
011F2964  add         esp,0Ch  
cs

다음과 같이 VC++ 의 경우, 스텍에 차곡차곡 넣어서 넘기는 일반 함수나 Static 함수 와 달리 맴버 함수에서 This Pointer(정확히는 객체 자신의 주소)를 ECX 레지스터를 이용해서 기억한다. (ECX 는 Counter용 아닌가;;)

뭐 결과론적으로 맴버 함수실행시 ECX 레지스터 에서 this pointer에 복사하기 때문에 넘기지 않는것은 아니지만 흔히 말하는 클래스명.함수(객체, 파라미터1, 파라미터2) 처럼 넘어가는것은 아니다.

이를 찾아보니..

https://stackoverflow.com/questions/1297205/the-c-implicit-this-and-exactly-how-it-is-pushed-on-the-stack

 

The C++ implicit this, and exactly how it is pushed on the stack

I need to know whether, when a class method in C++ is called, the implicit 'this' pointer is the first argument, or the last. i.e: whether it is pushed onto the stack first or last. In other words...

stackoverflow.com

 

VC++에서는 ECX 레지스터를 이용해서 넘기는것이 기본이고 __stdcall 키워드를 이용하면 우리가 아는것처럼 동작하게 된다고 한다. 참고로 G++ 은 최적화를 쓰지않으면 우리가 아는것처럼 동작한다고 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
 
class A
{
public:
    void __stdcall Fun(intint) {}
    static void Fun(A* a, intint) {};
};
 
void  Fun(A* a, intint) {}
 
int main()
{
    A a;
 
    cout << "Reg" << endl;
    Fun(&a, 12);
    a.Fun(12);
    A::Fun(&a, 12);
    return 0;
}
 
cs

 

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    //Fun(&a, 1, 2);
0084293B  push        2  
0084293D  push        1  
0084293F  lea         eax,[a]  
00842942  push        eax  
00842943  call        Fun (08416AEh)  
00842948  add         esp,0Ch  
    //a.Fun(1, 2); , __stdcall
0084294B  push        2  
0084294D  push        1  
0084294F  lea         eax,[a]  
00842952  push        eax  
00842953  call        A::Fun (08416B8h)  
    //A::Fun(&a, 1, 2);
00842958  push        2  
0084295A  push        1  
0084295C  lea         eax,[a]  
0084295F  push        eax  
00842960  call        A::Fun (08416A9h)  
00842965  add         esp,0Ch  
cs

 

보시다시피 어셈블리어 단에서 다른 함수와 동일하게 동작한다.

하지만 그렇다고 A::fun(*a) 처럼 사용은 안된다 ㅜ 이러게 사용하는 방법을 아는사람이 있다면 알려주시길 ㅜㅜ

 

 

 

728x90