Type Casting in C++

smsh0722·3일 전

기타 정리

목록 보기
3/7

★★★Cast 비교★★★

C++ Casting Operators

C++의 Type Casting (형 변환)은 값을 한 타입에서 다른 타입으로 변환하는 것. C++은 C-style cast보다 의도가 명확하고 안전한 4가지 전용 연산자를 제공한다.

Cast핵심 용도검사 시점
static_cast일반적인 타입 변환Compile Time (컴파일 타임)
dynamic_cast상속 구조에서 안전한 다운캐스팅Runtime (런타임)
const_castconst / volatile 변경한정자 변경
reinterpret_cast메모리를 다른 타입으로 재해석실질적 타입 안전성 없음

선택 기준

일반적인 타입 변환
→ static_cast

Base → Derived인데 실제 타입을 확인해야 함
→ dynamic_cast

const / volatile만 변경
→ const_cast

메모리/포인터를 완전히 다른 타입으로 해석
→ reinterpret_cast

핵심 암기

static = 타입 변환
dynamic = 런타임 타입 확인
const = const 제거/추가
reinterpret = 메모리 재해석


★★★static_cast★★★

기본

static_cast (정적 형변환)는 Compile Time (컴파일 타임)에 타입 관계를 검사하며, 서로 변환 가능한 타입끼리 명시적으로 변환한다.

float f = 3.5f;
int x = static_cast<int>(f); // 3

주로 숫자 변환, 상속 관계 변환, 사용자 정의 변환 등에 사용한다.

주요 용도

숫자 타입 변환

double d = 3.14;
int n = static_cast<int>(d);

double → int처럼 정상적인 타입 변환에 사용한다.

Inheritance (상속)

Derived d;
Base* b = static_cast<Base*>(&d); // Upcasting

Upcasting (업캐스팅)은 안전하다.

Downcasting도 문법상 가능하다.

Base* b = ...;
Derived* d = static_cast<Derived*>(b);

하지만 dynamic_cast와 달리 Runtime Type Check (런타임 타입 검사)를 하지 않는다.

즉, 실제 객체가 Derived가 아니면 위험하다.

Base* base = new Derived(); → 문제 없음
Base* base = new Base(); → 문제 있음

User-Defined Conversion (사용자 정의 변환)

생성자나 Conversion Operator (변환 연산자)를 호출할 수도 있다.

string s = static_cast<string>(obj);

해당 타입에 적절한 변환 연산자가 정의되어 있으면 이를 사용한다.

void* 변환

int x = 10;

void* p = static_cast<void*>(&x);
int* q = static_cast<int*>(p);

객체 포인터 ↔ void* 변환이 가능하다.

제한

char* c;
int* p = static_cast<int*>(c); // Error

서로 관련 없는 포인터 타입끼리는 변환할 수 없다.

또한:

  • const 제거 불가 → const_cast
  • 메모리 강제 재해석 불가 → reinterpret_cast
  • 런타임 타입 검사 없음 → 필요하면 dynamic_cast

핵심 정리

static_cast
= 컴파일 타임에 유효한 타입 변환인지 검사하는 명시적 형변환
  • 숫자 타입 변환
  • 상속 관계 변환
  • 사용자 정의 변환
  • void* 변환
  • Runtime Check X
  • dynamic_cast보다 빠르지만 Downcasting 안전성은 직접 보장해야 함

★★★dynamic_cast★★★

기본

dynamic_cast (동적 형변환)는 Runtime Type Checking (런타임 타입 검사)을 수행하는 형변환이다.

주로 Base → Derived Downcasting (다운캐스팅)에 사용한다.

Base* p = new Derived();

Derived* d = dynamic_cast<Derived*>(p);

실제 객체가 Derived인지 런타임에 확인한 뒤 변환한다.

조건

Downcasting에 사용하려면 Base가 Polymorphic Class (다형 클래스)여야 한다.

즉, 최소 하나의 virtual 함수가 필요하다.

class Base {
public:
    virtual ~Base() = default;
};

virtual 함수가 없으면 dynamic_cast를 통한 Downcasting은 컴파일 에러가 발생한다.

성공 / 실패

Pointer

Base* p = ...;
Derived* d = dynamic_cast<Derived*>(p);

if (d)
    // 성공

실제 객체가 Derived이면 정상 포인터를 반환하고, 아니면 nullptr을 반환한다.

Derived1 obj;
Base* p = &obj;

Derived2* d = dynamic_cast<Derived2*>(p);
// d == nullptr

### Reference

```cpp
Derived& d = dynamic_cast<Derived&>(base);

Reference는 nullptr이 될 수 없으므로 실패하면 std::bad_cast Exception (예외)을 발생시킨다.

RTTI

dynamic_cast는 RTTI (Run-Time Type Information, 런타임 타입 정보)를 이용한다.

dynamic_cast<Derived*>(base);

런타임에 실제 객체 타입을 검사하기 때문에 약간의 실행 비용이 있다.

변환이 확실하게 안전하다는 것을 이미 알고 있다면 static_cast가 더 가볍다.

RTTI 개념

RTTI (Run-Time Type Information, 런타임 타입 정보)는 실행 중 객체의 실제 타입을 확인하는 기능이다.

주로 Inheritance (상속), Polymorphism (다형성) 환경에서 사용한다.

Upcasting (업캐스팅)

Derived → Base 변환.

Derived d;
Base* b = &d;
  • 안전함
  • 보통 암시적으로 변환
  • 다형성을 구현할 때 기본적으로 사용

Downcasting (다운캐스팅)

Base → Derived 변환.

Base* b = new Derived;

Derived* d = dynamic_cast<Derived*>(b);

Base 포인터가 실제로 Derived 객체를 가리키는지 확인해야 하므로 Runtime Check (런타임 검사)가 필요하다.

if (d != nullptr)
    d->show();

잘못된 Pointer 다운캐스팅이면 nullptr을 반환한다.

핵심 정리

RTTI
 └─ 실행 중 실제 객체 타입 확인
      └─ dynamic_cast 등에 사용
  • Upcasting: Derived → Base, 안전하며 보통 자동
  • Downcasting: Base → Derived, 실제 타입 확인 필요
  • dynamic_cast: RTTI를 이용해 안전하게 Downcasting

즉, RTTI = 런타임에 객체의 실제 타입을 파악하기 위한 C++ 기능이다.

핵심 정리

Derived* d = dynamic_cast<Derived*>(base);
  • Runtime Type Checking (런타임 타입 검사)
  • 주 용도: Downcasting (다운캐스팅)
  • Base에 virtual 함수 필요
  • Pointer 실패 → nullptr
  • Reference 실패 → std::bad_cast
  • RTTI 사용 → static_cast보다 런타임 비용 존재

즉, dynamic_cast = 상속 관계에서 실제 객체 타입을 확인하면서 안전하게 형변환할 때 사용한다.


★★★const_cast★★★

기본

const_cast (상수성 변환)는 타입의 const / volatile 한정자만 추가하거나 제거한다.

const_cast<new_type>(expression)
int x = 10;
const int* p = &x;

int* q = const_cast<int*>(p);
*q = 20; // OK

원본 x가 실제로 non-const이므로 수정 가능하다.

진짜 const 객체는 수정 금지

const int x = 10;
const int* p = &x;

int* q = const_cast<int*>(p);
*q = 20; // Undefined Behavior

const_cast로 const를 제거하는 것 자체는 가능하지만, Originally Const Object (원래 const 객체)를 실제로 수정하면 Undefined Behavior (정의되지 않은 동작)이다.

주요 용도

Const Pointer / Reference 해제

void foo(int* p);

const int* p = ...;
foo(const_cast<int*>(p));

주로 const-correctness를 지원하지 않는 Legacy API (레거시 API)와 연동할 때 사용한다. 단, 해당 함수가 실제 값을 수정하지 않는다는 보장이 필요하다.

const Member Function 내부

void func() const {
    const_cast<MyClass*>(this)->value = 10;
}

const 멤버 함수의 this에서 const를 제거할 수 있다.

단, 실제 객체가 const라면 이를 통해 상태를 수정하는 것은 UB가 될 수 있다.

제한

const int* p;
char* q = const_cast<char*>(p); // Error

const_cast는 타입 자체를 변경할 수 없다.

  • const 추가/제거
  • volatile 추가/제거

만 가능하다.

핵심 정리

int x;                  // 실제 non-const
const int* p = &x;
*const_cast<int*>(p)=1; // OK

const int y = 10;       // 실제 const
const int* q = &y;
*const_cast<int*>(q)=1; // UB

const_cast = const/volatile 한정자만 강제로 변경.

가장 중요한 기준은 "포인터가 const인가?"가 아니라 "원본 객체가 실제로 const인가?"이다.


★★★reinterpret_cast★★★

기본

reinterpret_cast (재해석 형변환)는 같은 메모리/주소를 다른 타입으로 해석할 때 사용하는 저수준 캐스팅이다.

int x = 65;
int* p = &x;

char* c = reinterpret_cast<char*>(p);

p와 c는 같은 주소를 가리키지만, 그 메모리를 각각 int, char 관점에서 해석한다.

주요 용도

T* → U*
pointer → integer
integer → pointer

주로 다음과 같은 Low-Level Programming (저수준 프로그래밍)에서 사용한다.

  • Raw Memory (원시 메모리) 접근
  • Hardware / OS 연동
  • Legacy API 연동
  • 서로 관련 없는 Pointer 타입 변환

특징

int* p = ...;
char* c = reinterpret_cast<char*>(p);
  • 상속 관계가 없어도 변환 가능
  • Runtime Type Check (런타임 타입 검사) 없음
  • 실제 데이터 자체를 변환하는 것이 아니라 해석하는 타입을 변경
  • Type Safety (타입 안전성)가 매우 낮음

주의

캐스팅 자체가 가능하다고 해서 해당 포인터를 역참조하는 것까지 안전한 것은 아니다.

A* a = ...;
B* b = reinterpret_cast<B*>(a);

b->foo(); // 실제 메모리가 B가 아니라면 위험 / UB 가능

따라서 static_cast, dynamic_cast, const_cast로 해결할 수 없는 특수한 저수준 상황에서만 사용하는 것이 일반적이다.

핵심 정리

reinterpret_cast
= 메모리를 다른 타입의 관점으로 강제로 재해석
  • 타입 관계 검사 X
  • 런타임 검사 X
  • unrelated pointer 간 변환 가능
  • 매우 강력하지만 Undefined Behavior (정의되지 않은 동작) 위험이 큼

0개의 댓글