[1.5편] C++ 다운캐스팅이 위험한 이유: 메모리 구조와 포인터 관점에서 이해하기
🚀 들어가며
이전 글에서 dynamic_cast와 RTTI에 대해 살펴봤습니다.
그런데 여기서 한 가지 중요한 질문이 남습니다.
❓ 왜 C++에서 다운캐스팅은 위험하다고 할까?
단순히 “안전하지 않다”는 설명만으로는 부족합니다.
이번 글에서는 메모리 구조와 포인터 관점에서 다운캐스팅이 왜 위험한지 깊이 있게 살펴보겠습니다.
🔍 업캐스팅 vs 다운캐스팅
먼저 개념부터 정리해봅시다.
Base* base = new Derived();
✔ 업캐스팅 (자손 타입에서 부모 타입으로의 형변환)
Base* b = derived;
- 항상 안전
- 추가 정보 손실 없음
⚠ 다운캐스팅 (부모 타입에서 자손 타입으로의 형변환)
Derived* d = static_cast<Derived*>(base);
- 컴파일은 되지만 위험
- 실제 타입이 다르면 문제 발생
🧠 핵심: 메모리 구조
다음 클래스를 보겠습니다.
class Base
{
public:
int a;
};
class Derived : public Base
{
public:
int b;
};
메모리 구조
[ Base(a) ][ Derived(b) ]
👉 중요한 포인트:
Derived는 Base를 포함한 더 큰 구조
⚠ 잘못된 다운캐스팅
Base* base = new Base(); Derived* d = static_cast<Derived*>(base);
이 코드는 컴파일됩니다.
하지만, base 변수를 생성할 때 Base를 상속하는 Drived가 아니라 Base 타입으로 생성했습니다.
따라서 실제 메모리에는 Base만 담을 수 있는 공간만 할당이 되어 있습니다.
[ Base(a) ]
👉 그런데 우리는 이렇게 접근합니다:
d->b = 10;
💣 무슨 일이 벌어질까?
이건 단순한 오류가 아닙니다.
👉 결과:
- 다른 변수 덮어쓰기
- 메모리 손상
- 프로그램 크래시
- 예상하지 못한 힙(Heap) 오염 오류
👉 즉:
❗ Undefined Behavior (정의되지 않은 동작) -> 예상할 수 없는 결과가 발생합니다.
🔧 포인터 관점에서 보면
포인터는 단순히 메모리 주소를 저장하는 변수입니다.
Derived* d = (Derived*)base;
“이 주소를 Derived라고 믿고 쓰겠다”
라는 의미입니다.
👉 컴파일러는 아무것도 검증하지 않습니다.
🎯 왜 dynamic_cast가 필요한가
- 다시 말하면 다운 캐스팅을 할 때 “왜 타입 확인이 필요한가“를 생각해봐야 합니다.
Derived* d = dynamic_cast<Derived*>(base);
- 실제 객체 타입 확인
- 실패 시 nullptr 반환
👉 즉:
“정말 Derived인지 확인하고 캐스팅”
💡 핵심 정리
- 업캐스팅: 항상 안전
- 부모로의 형변환은 항상 안전.
- 메모리 관점에서 왜 안전한지도 정리해둘 필요 있음.
- 다운캐스팅: 타입 불일치 시 위험
- 부모 타입으로 저장했다가 자손 타입으로 형변환할 때 해당 메모리 공간이 형변환하려는 자손 타입으로 선언되었는지 확인하는 것이 필요함.
- 메모리 관점에서 왜 위험하고, 왜 확인이 필요한지도 함께 정리해둘 필요 있음.
- static_cast: 검증 없음
- dynamic_cast: 런타임 검사 수행
🔥 다음 단계
이제 자연스럽게 이런 질문이 나옵니다.
❓ 그렇다면 dynamic_cast 없이 안전하게 타입을 확인할 수는 없을까?
다음 글에서는:
👉 static 주소 기반 커스텀 RTTI 구현을 통해 dynamic_cast 없이 타입을 판별하는 방법을 다룹니다.
🎯 마무리
다운캐스팅의 위험성을 이해하면 왜 RTTI(RunTime Type Information)가 필요한 지를 이해할 수 있습니다.
따라서 C++에서 다운 캐스팅을 할 때는 실행 시점에 타입을 확인하는 dynamic_cast를 사용해야 합니다.
하지만, dynamic_cast는 여러 단점이 존재합니다.
이를 이해하면 왜 게임 엔진에서 RTTI를 직접 구현하는지 이해할 수 있습니다.
이 내용은 단순 문법이 아니라
👉 엔진 설계와 직결되는 핵심 개념입니다.
참고로 이 과정을 이해하면 언리얼 클래스/구조체 내부에 작성되는 GENERATED_BODY() 매크로의 원리를 이해할 수 있습니다.