COM, ATL

COM, ATL에서의 reinterpret_cast 의 쓰임새

디버그정 2008. 9. 11. 15:34

그동안 C++에서 reinterpret_cast를 배우긴 배웠는 데 실제로 써먹는 경우가 없었다.

그런데 컴포넌트 작성하면서 쓰게 될 줄이야~~~
커넥션 포인터 관련 얻는 부분의 코드 조각이다.
m_rgpCP[0] = new CConnectionPoint(reinterpret_cast<IUnknown*>(this), DIID__DIDragonEvents);

사전적 의미는 "재해석해서 캐스트"한다....
강제 형변환 캐스팅이다.

컴파일러는 약간 지능적이다.
클래스 형변환시 부모 - 자식 관계의 형태에서 포인터 관련코드 디버그해보면서 놀랐던 기억이 난다.
그 전까지 형변환으로 사용했던 것은 모두 reinterpret_cast(강제 형변환) 의미를 가진 것이었다.
이를테면 핸들값을 스파이로 알 경우.... HWND hWnd = (HWND)0x008763e3;
이런 식으로 사용했다.

상속의 경우 부모가 자식을 가리킬 수 있음의 관계를 알고자
(자식안에는 부모의 요소가 모두 갖춰져 있다.)
포인터의 이동을 확인해 보았다.

class C : public A, public B 의 경우
C객체를 생성하고 각각 A형 포인터, B형 포인터로 받아 보았다.
똑같은 객체인데 각각의 포인터 값(주소값)을 확인해 보면 다름을 볼 수 있다.
이 부분을 발견하고 화들짝 놀랬다. 그리고 형변환의 여러형태를 실질적으로 인식하는 기회가 되었다.
구체적으로 A형 포인터의 경우 A에서 물려받은 첫 변수(혹은 A 관련 가상함수 테이블)의 주소를 가리키고,
B형 포인터의 경우 B에서 물려받은 곳의 주소를 가리키고 있다.
따라서 A형 포인터로 A와 관계된 멤버들을 다룰 수 있고, B형 포인터로 B 관련 멤버들을 처리할 수 있게 된다.
컴파일러는 클래스 선언에서 각각의 클래스의 상속관계를 파악할 수 있으므로 이런 코드가 생성가능하게 된다.
이를테면 상속관계의 경우 포인터는 자동적으로 객체에서 해당 타입에 맞게 위치이동한다.
이로 인해 c++ 클래스의 상속관계의 원할한? 처리가 가능해지는 것이다....

이런 상속관계는 컴포넌트의 경우 자주 애매모호한 상황을 초래할 수 있다.
컴포넌트 작성시 다중상속이 태반이고, IUnknown은 약방의 감초처럼 모든 인터페이스의 부모가 된다.
IDispatch나 IConnectionPointContainer 등 표준 인터페이스 뿐 아니라
프로그래머 임의로 만든 커스텀 인터페이스에 이르기까지 모두 IUnknown이 최상위 베이스에 위치한다.
그러므로 다중상속하는 클래스가 인스턴스화할 때 IUnknown의 가상테이블은 여러군데에 걸치게 된다.
(참고로 IUnknown은 순수가상함수를 가진 추상 클래스이므로 실제로 가상테이블이 존재하는 형태로 나타난다.)
그러므로 이경우 (IUnknown*)this 형태로 쓰는 경우 어느 곳의 IUnknown을 가리키는 지
모호한 상황이 되므로 컴파일러가 에러를 뿜게 된다.

이 같은 상황에서 reinterpret_cast를 쓰는 것이다...이 때 this 객체의 주소를 강제캐스팅하니
가장 먼저 상속받은 인터페이스의 IUnknown 을 가리킬 것이다.
(참고로 구현 객체에 의해 모두 같은 내용으로 덮여지므로(순수 가상함수)
  다른 인터페이스의 IUnknown을 가리키더라도 상관없다.)
즉 컴파일러에게 모호한 상황을 제거하는 의미로 쓰는 것이다.