C++ 다운캐스팅이 위험한 이유: 메모리 구조와 포인터 관점에서 이해하기 (1.5편)

[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() 매크로의 원리를 이해할 수 있습니다.

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Please turn AdBlock off

Notice for AdBlock users

Please turn AdBlock off