최신목록

2015년 9월 25일 금요일

[상속]상속과 메모리 맵

상속의 개념은 알고 있었으나 상속되었을시 메모리 구조에 대해서는 최근 후킹시 펑션 포인터를 얻어오기 위해서 생각을 해보게 되었습니다.

이전에 캐스팅 연산자 포스팅에서 reinterpret_cast에 대해서 논하면서 테스트 해본 내용이
상속받은 클래스를 reinterpret_cast로 캐스팅 해서 기존 클래스 얻을 수 있는가 하는 것이었습니다. 기존 클래스를 얻을 수 있을거란 내 예상과는 달리 기존 클래스를 얻지 못한 경우도 있어서 확실한 내용이 아니라 포스팅에서는 언급하지 않았던 부분이었다.

여기서는 상속하였을때 파생클래스의 매모리 맵 구조를 알아보고 왜 그런결과가 있었는지에 대해 정리해보았다.

1. 단일상속
단일 상속은 클래스 하나를 상속받은 경우이며 두 클래스의 메모리 맵은 아래와 같다.




First 클래스를 상속받은 Second 클래스입니다.
WinDbg에서 메모리 구조를 확인하였습니다.
watch 창에서 Second 클래스 x에 대해서 각각 a, b, a1, b1에 대한 멤버 변수의 메모리 위치를 확인하였습니다. x 인스턴스는 0x00635748이고 멤버변수는 순서대로 배치되 있음을 확인할 수 있습니다.
이번엔 Third 클래스를 만들어서 Second 클래스를 다시 상속받아 보겠습니다.

x 인스턴스는 0x00575748 이고 멤버변수는 상속받은 순서대로 배치되 있음을 확인할 수 있습니다.

Third 클래스의 메모리구조를 보면 위 4개는 Second 클래스의 메모리 구조와 같고 위 4개는 First 클래스의 메모리구조와 같음을 확인할 수 있습니다.

그러므로 Third 클래스의 인스턴스를 First 클래스에 캐스팅하면 First 클래스가 되고 Second클래스에 상속하면 Second 클래스가 됨을 알 수 있습니다. 하지만 다중상속에서는 달라지는데요 아래에서 확인해보겠습니다.


2. 다중상속
다중상속은 하나가 아닌 여러개의 클래스로 부터 상속을 받는 경우를 의미합니다. 메모리맵을 확인해보겠습니다.


Third 클래스 x의 인스턴스는 0x00795748이고 역시 상속받은 순서대로 변수 배치가 되있음을 확인할 수 있다. 하지만 First클래스나 Second 클래스로 캐스팅할때는 얘기가 달라집니다.
위의 그림에서 보듯이 Second 클래스가 Third 클래스의 인스턴스 시작위치와 같지 않음을 알 수 있습니다. Second 클래스는 First 클래스를 포함하고 있지 않기 때문에 위와 같은 구조가 됩니다. 따라서 reinterpret 캐스팅시(in_s1 = 0x00795748)와 static 캐스팅시(in_s = 0x00795750) Second의 인스턴스 값이 다르게 되고 reinterpret 캐스팅은 다중상속에서는 매우 위험함을 알 수 있습니다.

3. 다이아몬드상속
다중상속 중 base 클래스가 같은 클래스를 다중상속받는것을 이야기합니다. 어떤일이 일어날가요? 중복되는 부분이 있을듯 한데요.

base 클래스인 First 클래스가 먼저 배치되고 그다음 상속받은 순서대로 Second, Third의 순서로 메모리에 배치됨을 볼 수 있다.
클래스에 변수만 있다면 아직까지는 순조롭고 원하던 데로 배치가 된듯합니다. 하지만 First 클래스에 함수가 있으면 어떻게 될까요?

First 클래스에 First_func 함수를 추가했더니 Second클래스와 Third 클래스에 모두 포함되게 되어 Fourth에서는 찾을 수가 없다는 컴파일 에러가 발생합니다.
이를 해결하기 위해서 가상상속이 나타나게 됩니다. 그럼 가상상속에 대해서 알아보겠습니다.

4. 가상상속
위에서처럼 base가 같은 클래스를 다중으로 상속 받을경우 base클래스는 한번만 상속받게 하기 위해서 virtual 키워드를 사용하여 가상상속을 사용합니다.

위의 코드처럼 virtual로 base클래스인 First클래스를 상속받았더니 x->Fust_func의 모호함이 사라지고 원하던대로 상속받은 모든 함수의 사용이 자유롭습니다.

댓글 없음:

댓글 쓰기