안녕하세요. 저에요.
저번에 다중 상속에서의 캐스팅 주의점에 대해 알아봤는데요.
오늘은 어떻게 그런 식으로 캐스팅이 동작하는지 알아볼 거예요. (대충 지루하고 현학적인 이야기다요)
C++스타일의 cast에는 여러 종류가 있는데요.
오늘은 그중에서 가장 많이 쓰이는 cast은 static_cast와 dynamic_cast에 대해 알아봅시다.
● static_cast
static_cast에 대해 많은 오해를 하고 있는 부분이 있어요.
그건 바로 static_cast는 컴파일 타임에 에러를 검출해 낸다는 것인데요.
근데 써보신 분들은 이런 경험을 겪으신 적이 있을 겁니다.
난 분명 static_cast를 썼고, 컴파일도 제대로 되었는데, 런타임 중에 static_cast에서 터졌어!!
분명 컴파일 타임에 에러를 검출해 준다 했는데... 컴파일이 되었는데 터졌어.... 뭐지... 뭔가 일어나고 있음...
이런 경우를 겪으신 분 계신가요? (전 없긴함)
제대로 알고 써야 합니다!!
static_cast는 컴파일 타임에서 A와 B에 대한 상속 관계에 대한 합리성만 판단합니다.
무슨 말인지 예시를 들어보자면, (대충 지루하고 현학적인 예시)
영희는 철수에게 생일선물을 주려고 한다.
영희는 철수가 닌텐도를 가지고 싶다는 이야기를 들었다..
하지만 철수가 무슨 기종을 가지고 싶은지 명확히 모른다.....
그래서 영희는 최신 기종인 닌텐도 스위치를 선물하기로 했다!
아뿔싸! 철수가 가지고 싶었던 기종은 닌텐도 DS였다!!!!
이런 상황인 것이에요.
런타임 중에 static_cast가 터졌다는 것은,
닌텐도 DS든, 닌텐도 Wii든, 닌텐도 스위치든 전부 닌텐도라 할 수 있는데요.
영희는 이런 상황에 닌텐도 스위치가 닌텐도라는 합리성만 판단하고 위험하게 닌텐도 스위치를 선물한 겁니다.
코드로 표현하자면 이런 거죠.
class Nintendo {};
class Nintendo_Switch : public Nintendo {};
class Nintendo_DS : public Nintendo {};
// 1. 철수: 아 닌텐도가 가지고 싶다!
Nintendo_DS* ds = new Nintendo_DS();
// 2. 영희: 음... 철수가 닌텐도가 가지고 싶다 했지...
Nintendo* nt = ds; // Nintendo로 업 캐스팅
// 3. 영희: 그럼 최신 기종인 닌텐도 스위치를 선물해야겠다!
Nintendo_Switch* sw = static_cast<Nintendo_Switch*>(nt); // 컴파일러는 허용, 그러나 런타임에서는 잘못된 다운캐스팅
컴파일러 입장에선 Nintendo_Switch* ⇐ Nintendo* 관계가 성립하므로 캐스트 자체는 허용되지만, 실제로 ds는 Nintendo_Switch의 주소를 가리키는 게 아니기 때문에 알 수 없는 동작을 하게 됩니다.(보통은 터짐)
그러면 이런 상황에서 안전하게 확인하는 방법이 뭘까요?
그건 바로 dynamic_cast입니다!
● dynamic_cast
dynamic_cast는 말 그대로 안전한 캐스팅입니다.
RTTI를 통해 하나하나 유효성 검사를 실시하기 때문인데요.
RTTI란?
Run-Time Type Information의 약자로, 런타임에서의 객체들의 정보를 뜻합니다.
때문에 dynamic_cast는 캐스팅 시, 해당 객체에 대한 타입과 캐스팅 대상 타입을 상속 트리를 통해 하나하나 검사합니다.
실패할 시 터지지 않고 반환 값을 nullptr을 반환해 실패 여부도 확인할 수 있죠.
그럼 무조건 dynamic_cast 써야겠네? 무적이네?
절!!! 대!!! 아닙니다!!!!
하나하나 검사하는데 빠를 리가 없겠죠?
그래서 dynamic_cast는 속도가 느립니다. 여러분 생각보다 훨씬 느려요.
그저 맞냐 or 아니냐를 따지는 과정이 말로는 간단하지만, 여러 상황에 대한 예외 처리 때문에 생각보다 복잡한 과정이 있다고 하더라구요.
심지어 상속 관계가 복잡하고 깊어질수록 더 느려지겠죠?
그래서 자체 엔진 제작할 때 일화를 들려드리자면,
GetComponent<>() 함수 같은 것들에 dynamic_cast를 썼었는데....
테스트해 보니까 속도가 좀 느리더라구요....
그래서 다음에 만들 엔진엔 타입별 ID를 해싱해서 부여하고, 해시 맵으로 성능향상을 노려볼까 합니다.
● 마치며
이런 과정을 통해 cast함수들은 오프셋을 받아와 주소 재보정을 거치게 됩니다.
그래서 저희는 캐스팅 후에 주소 값이 달라진 걸 저번 포스팅을 통해 확인했구요.
이상 저였습니다. 감사합니다.
'프로그래밍 > C++' 카테고리의 다른 글
[C++] 다중 상속에서의 업캐스팅, 그리고 포인터 보정(1) (0) | 2025.07.01 |
---|---|
[C++] 오늘의 삽질_inline 키워드에 대하여. (3) | 2024.12.19 |
[C++] 2진 리터럴(Binary literals)에 대해 알아보자.araboza (C++14) (2) | 2024.09.16 |
[C++] 오늘의 삽질 (0) | 2024.09.13 |
[C++] auto의 타입 추론에 대하여 알아보자.araboza (C++11) (0) | 2024.09.13 |