static_cast/dynamic_cast 등 목적별 캐스팅Dog* d = (Dog*)k; 같은 코드가 왜 UB인지 설명할 수 있다.dynamic_cast를 적용할 수 있다.class Knight {
public:
virtual ~Knight() = default;
int hp = 100;
int attack = 20;
};
class Dog {
public:
int age = 0;
};
Knight* k = new Knight();
Dog* d = (Dog*)k; // ❌ C-Style 강제 캐스팅
d->age = 10; // ❌ Knight 객체 메모리를 Dog로 해석해서 기록 (UB)
핵심:
Knight와 Dog는 상속 관계가 없는데도 억지 변환을 해버린 상태입니다.실제: Knight* k ──► [Knight 메모리 레이아웃]
강제: Dog* d ──► [동일 주소를 Dog 레이아웃으로 해석]
d->age = 10; 수행 시:
Knight 객체의 "어딘가"를 Dog::age 자리로 가정하고 기록
-> 잘못된 위치 쓰기 -> 데이터 오염/크래시/비정상 분기 (UB)
중요한 점: “정확히 어떤 멤버가 깨진다”는 보장할 수 없습니다. 바로 이 불확실성이 UB의 본질입니다.
(T*)expr는 너무 많은 변환을 한 번에 허용해서 위험합니다.기본 가이드:
static_castdynamic_cast + nullptr 체크reinterpret_cast (아주 제한적으로)const_cast (정말 필요한 경우만)다운캐스팅 안전 예시:
Item* item = GetItem();
if (Weapon* w = dynamic_cast<Weapon*>(item)) {
// Weapon으로 확인된 경우에만 접근
// w->GetDamage();
}
Dog* d = (Dog*)k;가 컴파일되더라도 왜 안전하지 않을까?dynamic_cast와 널 체크를 같이 쓰는 이유는?