본문 바로가기

문과 코린이의, [C. C++] 기록/C++ 이론

[문과 코린이의 IT 기록장] C,C++ - This포인터( 숨겨져 있는 this 포인터, 명시적으로 this 참조하기 )

반응형

[문과 코린이의 IT 기록장] C,C++ - This포인터( 숨겨져 있는 this 포인터, 명시적으로 this 참조하기 )



[ 클래스의 맴버 함수를 호출할 때, C++은 어떻게 호출할 객체(인스턴스)를 찾는가? ]

: 이 질문에 대한 정답은, this라는 숨겨진 포인터를 사용한다는 것이다.

- main함수 'simple.SetID(2);'에서 보면, SetID()맴버함수가 객체(인스턴스) simple에서 작동해야 한다는 것을 알고, m_ID는 simple.m_ID를 참조한다. 이 과정이 어떻게 작동되는지 알아보자.

 

 



  1. 숨겨져 있는 this 포인터
1) simple SetID(2);
// 이 코드는 맴버 함수 SetID()가 하나의 인수만 가지고 호출하는 것처럼 보이지만, 실제로는 두 개의 인수를 가지고 있다.

2) 이 코드를 컴파일하면 이와 같이 변환된다.
simple.SetID(&simple, 2);
// 즉, 맴버 함수의 인수로 객체(인스턴스)의 주소가 전달된다는 것이다.

3) 맴버 함수 호출에 매개변수가 추가되었으므로, 맴버 함수의 정의 역시 매개변수를 하나 더 받도록 수정되어야 한다.

-> 이 함수의 정의는 컴파일러에 의해, 다음과 같이 변환된다.

Void SetID(int id){ // Void SetID(SImple * const this, int id)
m_ID = id; // this->m_ID = id;
}
// 즉, 컴파일 할 때 컴파일러this를 함수에 추가한다. 이 this포인터는 맴버 함수가 호출된 객체의 주소를 가리키는 숨겨진 포인터다.


다시말하자면, 맴버 함수 안에서, 모든 클래스의 맴버 변수의 참조 또한 this->가 추가되는 형식으로 변환된다.
따라서 위 코드에서는 m_ID에 대한 참조가, this -> m_ID로 변환된 것이며, 결과적으로 this->m_ID는 simple -> m_ID가 되는 것이다.

[ 컴파일러 수행 과정 요약 ]
1. simple.SetID(2); // 컴파일러는 실제로 simple.SetID(&simple, 2);로 변환해서 호출한다.
2. SetID()맴버 함수 내부에서, this포인터는 simple 객체의 주소를 가진다.
3. SetID()내부의 모든 맴버 변수 앞에는, tihs->가 붙는다. 따라서 m_ID=id는, 실제로 this->m_ID = id;로 변환된다.

- 그러나 이 과정은 사용자가 직접 해줘야 하는 것이 아니라, 컴파일러에 의해 자동으로 수행되므로 세세하게 기억할 필요는 없다. 
- 그렇지만, 기억해야 할 것은 모든 맴버 함수는 함수가 호출된 객체(인스턴스)를 가리키는 this포인터를 가지고 있다는 것이다.
- 따라서, this포인터는 어떤 객체(인스턴스)에서 맴버 함수를 호출했는지에 따라 가리키는 주소가 다르다는 것이다.
 ex ) Simpl A(1)[&A호출] 과 Simple B(1)[&B호출] 의 주소가 다르다는 것이다.
- 또한, this는 함수 매개 변수에 불과하므로, 클래스에 메모리 사용량을 추가하지 않는다. (함수가 실행되는 동안에만 스택에 매개변수가 쌓인다.)




 2. 명시적으로 this를 참조해야하는 경우가 있다.
- this를 명시적으로 참조할 수 있는, 2가지 경우가 있다.


1) 맴버 변수와 이름이 같은, 매개 변수를 가진 생성자(또는 맴버 함수)가 있는 경우
: this를 사용해서 이 둘을 구분할 수 있다.
ex )

이 예제 코드에서는, 맴버 변수인 private에 있는 int data와, 함수 매개변수 (int data)가 이름이 같은 것을 볼 수 있다. 이럴 때 this->를 명시적으로 사용해서 구분할 수 있다.

ps. 그렇지만, 맴버변수 이름에 m_(member의 약자)과 같은 접두사를 이용해서, 이름 중복을 방지하는게 좀 더 바람직하다.



2) 맴버 함수 체이닝 기법
: 클래스 맴버 함수가 작업 중이던 객체(인스턴스)를 반환하는 방식이 유용할 경우가 종종 있다. 이 방법을 사용하면, 객체의 여러 맴버 함수를 연속해서 호출할 수 있다.
ex )

// 이 예제를 보면, 객체(인스턴스)를 계산하는데, 더 많은 코드를 작성해야하는 것을 볼 수 있다.

- 그러나 만약 각각의 맴버 함수가 this를 반환한다면, 이 문제를 해결할 수 있는 체이닝 기능이 있는 새로운 버전의 Calc를 만들 수 있다.

- 이 코드에서 Add(), Sub(), Mul() 맴버 함수가, *this를 반환하는 것을 볼 수 있다.
- 각 맴버함수가 호출되면서, c객체의 m_value를 수정하고 *this를 반환함으로써 자기 자신(인스턴스)를 반환한다. 따라서, 3줄이었던 코드를, 한 줄로 바꾸어 처리할 수 있다.


[ 요약 ]
- this포인터모든 맴버 함수에 추가되는 매개변수이다.
- 호출된 객체의 주소를 가리키는, 상수 포인터이다.
- 상수 포인터 자료형이므로, 포인터 자체가 다른 것을 가리키도록 할 수는 없다.
- 맴버 변수 이름과 맴버 함수의 매개변수 이름이 같으면, 명시적으로 this를 참조해서 구분할 수 있다. (ex. this->data = data;)
- *this를 반환하는 방식으로, 함수 체이닝 기법을 만들 수 있다.

* 유의사항
- 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다.
- 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다.
- 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :)
반응형