윈도우 소켓에서 reinterpret_cast라는 캐스팅을 처음 봐서 캐스팅 종류에 대해 알아보고자 포스팅했습니다.

🎯 캐스팅

캐스팅(Casting)이란 어떤 데이터의 자료형(Type)을 다른 자료형으로 변환하는 것을 의미합니다.

캐스팅의 종류로는 암시적 변환(Implicit)과 명시적 변환(Explicit) 두 가지로 나눌 수 있으며 암시적 변환은 컴파일러가 자동으로 변환해 주고 명시적 변환은 프로그래머가 직접 변환하는 것을 말합니다.

C++에서 캐스팅은 아래 네 가지 캐스팅 키워드로 할 수 있으며 버전에 따라 캐스팅 방법이 추가됐을 수 있습니다.

💡 info

Q. 왜 C 스타일의 형 변환을 사용하지 않을까?

A. C 형 변환 스타일은 강력하지만 안전하지 않습니다. 한 가지 예를 들어보면,

1
2
3
4
const int number = 10;    // 상수 선언
int* p = &number;         // ⚠️ const int*를 int*로 받음

*p = 20;                  // 💀 역참조해서 변경

위와 같은 상황이 발생할 수 있고, 프로그램 버그로 이어질 수 있습니다.

static_cast

💡 일반적으로 가장 많이 사용하는 캐스팅

컴파일러가 허용하는 일반적인 타입 변환을 명시적으로 수행합니다.

  1. 숫자 타입 변환

    1
    2
    3
    4
    
    double value = 3.14;
    
    int result = static_cast<int>(value);
    // result = 3
    
    • ⚠️ 타입 크기가 큰 곳에서 작은 곳으로 캐스팅 될 경우 데이터 손실 위험!
  2. void* 변환

    1
    2
    3
    4
    
    int value = 10;
    
    void* ptr = &value;
    int* intPtr = static_cast<int*>(ptr);
    
  3. 상속 관계 변환

    1
    2
    3
    4
    5
    6
    7
    
    class Base {};
    class Derived : public Base {};
    
    Derived derived;
    
    Base* base = &derived;                      // Upcast
    Derived* ptr = static_cast<Derived*>(base); // Downcast
    
    • ⚠️ 다운 캐스팅할 때 실제 객체가 Derived인지 런타임에서 확인하지 않음!

dynamic_cast

💡 런타임 타입 검사

상속 관계에서 실제 객체의 타입을 확인하면서 변환할 때 사용합니다.

일반적으로 부모 객체에 하나 이상의 virtual 함수가 있어야(RTTI1) 합니다.

  1. 포인터 변환
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    Base* base = new Derived();
    
    Derived* derived = dynamic_cast<Derived*>(base);
    
    if (derived) {
        derived->Execute();
    }
    
    delete base;
    
  2. 참조 변환
    1
    2
    3
    4
    5
    6
    7
    8
    
    Base& base = someObject;
    
    try {
        Derived& derived = dynamic_cast<Derived&>(base);
    }
    catch (const std::bad_cast&) {
        // 변환 실패
    }
    

const_cast

⚠️ 사용 주의

const와 volatile 한정자를 추가하거나 제거합니다. 객체에 대한 무결성 검사를 하지 않습니다.

1
2
3
const int value = 10;

int* ptr = const_cast<int*>(&value);

reinterpret_cast

💀 가장 주의가 필요한 캐스팅

서로 관련 없는 타입 사이에서 포인터나 참조 등을 재해석할 때 사용합니다.

  1. 타입 재해석
    1
    2
    3
    4
    5
    
    int value = 10;
    
    int* ptr = &value;
    
    char* charPtr = reinterpret_cast<char*>(ptr);
    
  2. 포인터 → 정수형 or 정수형 → 포인터
    1
    2
    
    std::uintptr_t address =
        reinterpret_cast<std::uintptr_t>(ptr);
    

📝 마무리

캐스팅은 가급적이면 C++ 캐스팅 방법의 static_cast와 dynamic_cast를 사용하고 특수한 경우에는 reinterpret_cast를 사용해야 할 것 같습니다. 👍


  1. RTTI(Run-Time Type Information 또는 Run-Time Type Identification)는 프로그램이 실행 중(Runtime)에 객체의 실제 타입(자료형)을 확인하거나 식별할 수 있도록 해주는 C++ 메커니즘입니다. ↩︎