C언어에 객체가 없듯이 다른 객체지향 패러다임을 채용한 다른 언어들 또한 유저 입장에서 객체를 설계했지 컴퓨터한테 객체를 들먹일 수는 없을 것이다.

C++에서 짤막한 프로그램을 작성해 오브젝트 파일로 컴파일 해보았다.

class MyClass {
private:
    int member_int;
public:
    MyClass(int m_int)
        : member_int{m_int} {};
    void increment(){
        member_int++;
    }
};

int main(void) {
    auto c = MyClass{3};
    c.increment();
    return 0;
}

좋아, 이제 *_*ZN7 MyClass 9 increment Ev 의 내용물을 읽어보자.

내가 봐야할 부분은 과연 increment 라는 함수가 과연 파라미터로 MyClass 포인터를 가지고 가는지다. 왜냐하면 결국 모든 메서드들이 사실 전역함수로 통한다면 멤버들을 어떻게 접근하겠는가? 바로 포인터를 사용할 것이다. 따라서 함부로 단언할 순 없겠지만 컴파일러는 둘 중 한 가지 방법을 사용할 것이다.

  1. 파이썬 메서드의 첫번째 파라미터와 같이 프로그래머 몰래 파라미터 하나를 심어놓았을 것이다.
    1. 근데 사실 내가 알기론 파라미터의 처음 몇 개 까지는 디폴트로 레지스터에 저장한다고 들었다. (Unix계열만 그랬던 것 같다) 그렇다면 아래 2번과 별반 다를 것이 없을 것이다.
  2. 그냥 레지스터 하나를 사용해서 MyClass 포인터 변수를 저장했을 것이다.

파이썬 메서드의 첫번째 파라미터는 반드시 Self 가 들어간다. 그 말은 곧 파이썬도 내부적으로는 메서드를 단순한 함수 취급하는데, 멤버접근을 위한 포인터가 필요했을 뿐이다.

컴퓨터는(정확히는 바이너리는) 클래스에 대해서 아무것도 모르기 때문에 접근지정자 private, public또한 모를 것이다. 그냥 모든 데이터가 전역적으로 덩그러니 놓여있고 클래스나 구조체나 할 것 없이 단지 컴퓨터 입장에선 단지 그 데이터의 위치와 길이, 오프셋에만 관심이 있을 뿐이다.

조금 더 뜯어보자

mainMyClass::increment 부분을 조금만 더 뜯어보자.

0000000000000000 <main>:
   0:	f3 0f 1e fa          	endbr64 
   4:	55                   	push   %rbp
   5:	48 89 e5             	mov    %rsp,%rbp
   8:	48 83 ec 10          	sub    $0x10,%rsp
   c:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  13:	00 00 
  15:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
  19:	31 c0                	xor    %eax,%eax
  1b:	48 8d 45 f4          	lea    -0xc(%rbp),%rax
  1f:	be 03 00 00 00       	mov    $0x3,%esi
  24:	48 89 c7             	mov    %rax,%rdi
  27:	e8 00 00 00 00       	callq  2c <main+0x2c> # 여기가 MyClass 생성자 호출하는 부분인가
  2c:	48 8d 45 f4          	lea    -0xc(%rbp),%rax
  30:	48 89 c7             	mov    %rax,%rdi
  33:	e8 00 00 00 00       	callq  38 <main+0x38> # 여기가 increment 호출하는 부분인가
  38:	b8 00 00 00 00       	mov    $0x0,%eax
  3d:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
  41:	64 48 33 14 25 28 00 	xor    %fs:0x28,%rdx
  48:	00 00 
  4a:	74 05                	je     51 <main+0x51>
  4c:	e8 00 00 00 00       	callq  51 <main+0x51>
  51:	c9                   	leaveq 
  52:	c3                   	retq   

0000000000000000 <_ZN7MyClass9incrementEv>:
   0:	f3 0f 1e fa          	endbr64 
   4:	55                   	push   %rbp
   5:	48 89 e5             	mov    %rsp,%rbp
   8:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
   c:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  10:	8b 00                	mov    (%rax),%eax
  12:	8d 50 01             	lea    0x1(%rax),%edx
  15:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  19:	89 10                	mov    %edx,(%rax)
  1b:	90                   	nop
  1c:	5d                   	pop    %rbp
  1d:	c3                   	retq   

Implementing Functions in x86 Assembly

어셈블리에서 함수를 어떻게 다루는지 설명하는 글을 읽고 다시 돌아오자…