Programming - cpueblo.com

this 완전 박살내버리기. 박재석


글쓴이 : 유광희 날짜 : 2002-05-13 (월) 21:26 조회 : 15953
#1860 박재석 (tong ) [강좌] this 완전 박살내버리기. 12/22 08:29 224 line 안녕하세요?====================================================== 이번에는 저번에 강좌해드린 C++강좌에서 미비하게 다루었던,,그래 서, 이해하기 힘들었을것 같은 this포인터를 한층 심도있게 다루겠읍 니다. 많은 분들이 C++을 배우면서 헤깔리시는 부분이 this포인터와 virtual예약어의 사용일 것입니다. 여기서는 this포인터에 대해서만 공부하겠읍니다. 참고로, 아래 내용은 제가 최대로 쉽게 설명 드릴려고 노력을 했습니 다. 그러나, C++의 클래스에대한 기초적인 사항을 알고 계셔야 하며, C에서의 구조체 정도도 쬐끔 알고 계셔야 더 쉽게 이해하실수 있을겁 니다. 이제, 본격적으로 this포인터를 까부셔 봅시다. ================================================================= 클래스는 멤버와 멤버함수로 구성됩니다. 아래 예를 보세요.. +--------------------------------------------+ | class tong { | | int x,y; | | public: | | int sub() { ... }; | | }; | | | | void main() | | { | | tong a,b,c; \\\\ (1) | | }; | (예1) +--------------------------------------------+ (예1)의 (1)은 객체(==변수) a,b,c를 tong형으로 선언하고 있는것입 니다. 이렇게 선언을 하면, a,b,c는 tong의 구조를 갖습니다. 즉, 멤 버 2개, 멤버함수 1개를 갖고 있는 구조를 하게 된다는 말입니다. 한편, 위와같이 선언을 하게되면, a에대한 멤버(x와 y), b에 대한 멤 버(x와 y), c에대한 멤버(x와 y)가 각각 메모리의 데이터 세그먼트라 는 영역에 할당이 됩니다. 그러나, sub()멤버함수에 대해서는 멤버처 럼 a,b,c 각각에 대해 각각 할당을 하는것이 아니라, '코드세그먼트' 라는 영역에 유일하게 하나만 자리를 잡습니다. 그리고, 참조를 할때 마다 a,b,c 세개의 객체가 공용으로 사용하게 되는 것입니다. 즉, 그 림으로 나타내면, a b c | | | | | +-------------------------------+ | +-----------------------------+ | | | | | +-----+ +--+--+ +--+--+ +-----------------------+ x,y | | x,y | | x,y | +--+--+ +--+--+ +--+--+ | | | +--+--------+--------+--+ | sub() 멤버함수 | +-----------------------+ 위 그림에는 약간의 오류가 있으나, 제가 위에서 설명한 내용을 그 림으로 나타내기 위해 부득이하게 나타낸 것입니다. 즉, 그림에서 볼 수 있듯이 x,y에해서는 a,b,c가 각각 할당을 하고 있으나, sub()에 대해서는 공용하고 있읍니다. 우선 객체(변수)를 할당하는 법에 대해서는 이정도로 해 두지요.. 이제 this포인터를 본격적으로 설명 드리죠.. ================================================================= 우선 (예)를 들어 드리죠!!!! +---------------------------------------------------+ | #include <stdio.h> | | | | struct tong { | | int x,y; | | int sub() { return(x+y); } | | }; | | | | void main() | | { | | tong a,b,c; //(1) | | a.x=10; a.y=20; | | b.x=30; b.y=40; | | c.x=50; c.y=60; | | printf("%d %d %d\\n",a.sub(),b.sub(),c.sub()); | | } | (예2) +---------------------------------------------------+ 너무 어려운 소스를 예로 든것 같은 생각이 저의 뇌를 퍽퍽 치고 지 나가는것 같군요..(엔돌핀 101마리 사망!..으흐흑..불쌍한엔돌핀..) 이제 this의 의미에 대해서 알아보지요.. this포인터는 C++컴파일러에 내정된 포인터로써 프로그래머가 따로 정의해줄 필요도 없고 정의해 주어서도 않되는 고유 포인터 입니다. 이 포인터는 언제 발생하냐? 클래스(구조체,공용체 포함)내부에 정의 ?낮거나, 선언되있는 '멤버함수'를 프로그램상에서 호출하여 사용할때 발생합니다. (예2)의 경우는 main()함수에서 쓰인 printf()함수속의 인자중에 그 멤버함수(sub())들을 호출하고 있읍니다. 한편, 포인터의 역할은 '주소'를 저장하는 역할을 한다고다들 알고 있을겁니다. 그럼, this포인터는 구체적으로 어떤 주소가 저장하는가? ...바로, 각 '객체의 주소'가 저장됩니다. 객체란 뭐냐? (예2)과 같이 tong이라는 클래스가 있다고 합시다. 그 리고, 변수 a,b,c가 (1)처럼 선언되 있다고 합시다. 즉, a,b,c는 tong 형의 변수가 됩니다. 이 변수들(a,b,c)을 바로 객체라고 합니다. this포인터는 그럼 a,b,c의 주소를 모두 한꺼번에 저장하느냐? 아닙 니다. 위에서도 말했듯이 단지, 클래스 내부에 있는 멤버함수를 호출 할때 비로소 this포인터가 그 기능을 발휘하는 겁니다. 그럼, 여기서 한가지 문제가 생깁니다. 위에서 설명한것처럼 sub()라 는 멤버함수는 '코드세그먼트'라는 영역에 유일하게 자리를 잡고 있으 면서, a,b,c가 공용하게 됩니다. 즉, sub()라는 함수는 1개 밖에 없다 는 이야기지요. 그러나, 아까 위에서 설명했듯이 멤버들은 a,b,c에 각 각 할당이 된다고 했읍니다. 즉, a도 x,y를 할당하고 있고, b와 c도 각각 자기 자신의 고유한 x,y를 할당해 놓고 있다는 이야기 지요. 그 래서 a의 x와 b의 x와 c의 x는 모두 다릅니다. 즉, x에 어떤 값이 들 어가느냐에 달려 있는 것이지요.. (예2)의 경우 a.x에는 10이 들어가 있고, b.x에는 30이 들어가 있군요. 물론, c.x에는 50이 들어가게 됩 니다. 이처럼 멤버는 각 객체를 구별하고 특징 지어주는 구별자로써의 역할도 한다고 할수 있읍니다. 그런데, (예2)의 sub()를 보세요. 이 sub()멤버 함수는 멤버 x와 y를 사용하는 함수입니다. 즉, 멤버 x와 y를 재료로 사용한다는 얘기지요. 그럼, sub()가 사용하는 멤버인 x와 y는 어떤 객체의 멤버인가? a의 멤버인가? b의 멤버인가? c의 멤버인가? 클래스 자체로써는 도처히 알수 없는 일이지요. C언어와 같은 경우 '.' 또는 '->'를 사용하여 'a.x'라는 식으로 x가 a의 멤버임을 알려주는 방법을 사용하여 구별하 고 있읍니다. 그럼 객체 지향언어인 C++은 어떻게 '.' 또는 '->'를 사 용하지 않고도 구별을 할수 있는가? 바로 this포인터가 그 역할을 담 당하고 있읍니다. 설명을 하자면, sub()를 호출하게 되면 컴파일러는 sub()를 누가 호 출했는지 검사를 합니다. 만일 a.sub()라고 했다면, 컴파일러는 '음~~ 객체 a가 sub()를 호출하고 있구나!' 라고 알아냅니다. 그리고나서, 컴파일러 자체가 마련하고 있던 this라는포인터에 객체 a가 할당하고 있는 부분의 시작주소 저장하게 됩니다. 즉, this포인터가 a를 가리키 게 된다는 이야기 입니다. 한편, 알아둘게 있습니다. 클래스 내부에서 멤버함수들이 사용하고 있는 멤버들은 그냥 멤버들이 아니라는 겁니다. 그 멤버들은 'this가 가리키고 있는 "객체"의 멤버들'입니다. 따라서, this포인터가 컴파일러에 의해 a를 가리키게 되면, 클래스 내부에 사용된 모든 멤버함수들은 객체 a가 가지고 있는 멤버들을 사 용해 자신의 기능을 발휘하는 것입니다. 즉, (예2)의 경우 sub()멤버 함수는 a의 멤버인, '10을 저장하고 있는 x'와 '20을 저장하고 있는 y'를 사용해 기능을 발휘하게 되는 것이지요. 즉, a의 맴버들을 재료 로 한다는 이야기 지요. 여기 까지 이해하셨다면, this포인터의 89.723% 는 마스터 하신겁니 다. 다시, 본론으로 들어 가서... (예2)의 tong클래스의 멤버함수 sub()를 다시 정의해 보겠읍니다. int sub() { return ((this->x) + (this->y)); } // (예3) (예3)은 (예2)의 tong클래스의 멤버함수 sub()와 완전 동일한 기능을 합니다. 즉, (예2)의 것을 (예3)처럼 고쳐써도 무방하다는 이야기지 요. 왜 이런 현상이 허용되는가? 원래 컴파일러가 컴파일을 하게 되 면, (예2)의 형태를 (예3)의 형태로 바꾸어서 .OBJ화일을 만들어 줍니 다. 그러니까, 우리는 (예2)처럼 사용하지만, 원래 오리지날이 (예3) 의 형태라는 것이지요.. 그러나, C++에서 클래스 내부에 사용되는 모 든 멤버들은 'this가 가리키고 있는 멤버'로 취급되므로, 구태여 (예 3)처럼 해줄 필요가 없습니다. 아까도 설명드렸듯이 this포인터에는 객체의 주소가 저장된다고 했습 니다. 여기서도 객체 a를 예로 들어봅시다. 만일 a가 sub()를 호출했 다면, 즉, a의 멤버들를 재료로 하는 sub()를 호출했다면, (예3)에서 쓰인 this->x는 a.x와 같은 의미가 됩니다. 즉, 'this->'가 'a.'의 역 할을 한다는 이야기 입니다. 이것은 다시 'this'가 '&a'와 같음을 알 게 해줍니다. 보세요.. 아까 설명해 드린 내용과 아주 일치하지요? 즉, this에는 a의 주소가 들어 간다는 이야기를 반복 설명드린 겁니 다. 아까 'this->x는 a.x와 같은 의미' 라고 했는데, 여기서 '같은의 미'라는 이야기는 의미만 같다는 얘기지, 바꿔써도 좋다는 얘기는 아 닙니다. 여기 까지 이해 하셨으면, this포인터의 96.3045%를 이해하신 겁니 다. 아까, 컴파일러는 this포인터를 자체적으로 마련해두고 있다고 했읍 니다. 그럼 어떤 형식으로 마련해 두고 있는가? (예2)의 경우는 다음 처럼 마련해 두고 있읍니다. tong* const this; 즉, 내부적으로 <클래스명>* const this; 위 처럼 자동적으로 선언을 해버린다는 이야기 입니다. 그럼 위 선언 은 어느 위치에서 암시적으로 선언되는가? 멤버함수내부에 각각 하나 씩 선언하고 있읍니다. 만일,(예2)에서 sub()함수 말고도 func()함수 jaj()함수가 멤버함수로써 정의되 있다고 가정한다면, int sub() { tong* const this; .. .. } void func() { tong* const this; .... } int jaj() { tong* const this; . . . . } 위 처럼 멤버함수 각각에 자동적으로, 암시적으로 this포인터를 선언 하고 있게끔 만들어 준다는 이야기지요. 여기서 알수있는 것은 this포 인터는 각 멤버함수의 지역변수라는 것입니다. 이것은 별로 중요한 내 용은 아닙니다만, 참고로 알아 두세요. 여기까지 이해를 하셨다면, 당신은 천재 입니다. this포인터의 99.994%를 이해하신 겁니다. 천재님! 정말 존경 스럽군요. (그럼, 천 재를 만든 나는 ?)..으흐흐... 이제 마지막으로 정리를 하도록 하지요.. 1.한 객체가 멤버함수를 호출할때는 this포인터가 자동적으로 설정된 다. 2.일단, this포인터가 설정되면, 이제 멤버함수는 this포인터가 가리 키고있는 객체의 멤버들을 재료로하여 기능을 발휘한다. 이상 입니다. 이것이 this포인터의 100% 입니다. 더 복잡하게 응용하는 경우도 있으나, 이정도면, this포인터를 사용 하는데는 별 어려움이 없을것으로 믿습니다. 그럼, 다음을 기약하며,,.......... 안녕히 계십시요. = 깡 t&#111;&#110;g 올림 =