1. 정적 캐스트 (static_cast)
개념:
static_cast는 컴파일 타임에 수행되는 타입 변환 연산자입니다.
- 기본적으로 안전한 변환에 사용되며, 명시적으로 허용된 변환만 가능합니다.
특징:
- 기본 자료형 간의 변환:
- 상속 관계에서 부모-자식 간 변환:
- 예:
Player* ↔ Knight* (상속 관계에서의 업캐스팅, 다운캐스팅).
void* 포인터를 다른 타입의 포인터로 변환.
예제: 기본 자료형 간 변환
int hp = 100;
int maxHp = 200;
float ratio = static_cast<float>(hp) / maxHp;
분석:
static_cast<float>(hp)는 int를 float로 변환.
- 안전성:
int에서 float로의 변환은 데이터 손실 위험이 없으므로 안전합니다.
- 컴파일러가 명시적으로 허용된 변환이므로 런타임 오류 가능성이 없습니다.
예제: 상속 관계에서 변환
class Player {};
class Knight : public Player {};
Knight* k = new Knight();
Player* p = k;
Knight* k2 = static_cast<Knight*>(p);
분석:
Player* p = k;
- 자식 클래스(
Knight*)를 부모 클래스(Player*)로 변환. 업캐스팅.
Knight* k2 = static_cast<Knight*>(p);
- 부모 클래스(
Player*)를 자식 클래스(Knight*)로 변환. 다운캐스팅.
- 상속 관계가 명확하므로 컴파일러는 변환을 허용.
주의:
static_cast는 런타임 검사를 수행하지 않음.
- 부모 포인터(
Player*)가 실제로 Knight*가 아니면, 변환된 포인터로 잘못된 메모리 접근이 발생할 수 있음.
2. 동적 캐스트 (dynamic_cast)
개념:
dynamic_cast는 런타임 타입 검사(RTTI, Run-Time Type Information)를 수행하며, 상속 관계에서의 안전한 형 변환을 보장합니다.
- 부모 클래스에서 자식 클래스로의 다운캐스팅 시 사용.
특징:
- 상속 관계에서만 사용 가능.
- 런타임에 타입이 맞지 않으면
nullptr을 반환.
- 가상 함수(virtual)를 가진 클래스에서만 사용 가능.
- 성능이 중요한 코드에서는 지양 (런타임 비용 발생).
예제: 안전한 다운캐스팅
class Player { virtual ~Player() {} };
class Knight : public Player {};
Player* p = new Knight();
Knight* k2 = dynamic_cast<Knight*>(p);
if (k2 != nullptr) {
} else {
}
분석:
Player* p = new Knight();
Knight 객체를 생성하고 부모 클래스 포인터로 저장.
dynamic_cast<Knight*>(p)
- 런타임에
p가 실제로 Knight* 타입인지 검사.
- 타입이 맞지 않으면
nullptr을 반환.
- 안전성:
- 런타임 검사로 인해 잘못된 타입 변환을 방지.
3. 상수 캐스트 (const_cast)
개념:
const_cast는 const 또는 volatile 속성을 제거하거나 추가합니다.
- 주로
const로 선언된 변수의 수정이 필요할 때 사용.
예제: const 제거
const int x = 10;
int& y = const_cast<int&>(x);
y = 20;
분석:
const_cast<int&>(x)는 x의 const 속성을 제거하여 int&로 반환.
y = 20;
- 주의:
const 속성을 제거한 변수의 값을 변경하면 정의되지 않은 동작(UB, Undefined Behavior)이 발생할 수 있음.
- 실제 메모리가
const로 보호되지 않은 경우에만 안전.
4. 재해석 캐스트 (reinterpret_cast)
개념:
- 비트 수준에서 데이터를 다른 타입으로 해석.
- 주로 포인터 타입 변환에 사용되며, 매우 강력하지만 위험함.
예제: 포인터 변환
class Dog {};
Dog* dog = new Dog();
int64_t address = reinterpret_cast<int64_t>(dog);
분석:
Dog* dog는 Dog 객체의 포인터를 저장.
reinterpret_cast<int64_t>(dog)는 포인터를 정수형으로 변환하여 메모리 주소를 저장.
- 사용 예:
- 시스템 프로그래밍, 메모리 매핑 등에서 활용.
예제: 잘못된 사용
class Dog {};
class Cat {};
Dog* dog = new Dog();
Cat* cat = reinterpret_cast<Cat*>(dog);
분석:
reinterpret_cast<Cat*>(dog)는 Dog 객체를 Cat 포인터로 해석.
- 위험성:
- 두 클래스가 아무런 관계가 없으므로,
cat을 통해 잘못된 메모리에 접근하면 프로그램이 충돌.
5. 캐스팅 방법 비교
| 캐스팅 방법 | 특징 | 사용 예 |
|---|
static_cast | 컴파일 타임 변환, 안전한 변환만 허용. | 기본 자료형 변환, 상속 관계의 업캐스팅 및 다운캐스팅. |
dynamic_cast | 런타임 타입 검사 수행, 안전한 다운캐스팅 제공. | 상속 관계에서 자식 클래스 타입 확인. RTTI 사용. |
const_cast | const 또는 volatile 속성 제거. | const 변수의 수정이 필요할 때 사용. |
reinterpret_cast | 비트 수준에서 데이터 변환. 매우 강력하지만 위험함. | 포인터 타입 변환, 메모리 주소 저장, 하드웨어 레지스터 접근 등. |