타입 변환

Dohun Lee·2025년 8월 9일

C/C++

목록 보기
21/34

C/C++에서 변수의 타입 변환

타입 변환의 종류에는 값을 유지하기 위해 해당 데이터의 변형이 발생하는 값 타입 변환과 데이터의 변형이 발생하지 않고 데이터가 그대로 유지 된 채로 타입이 변환되는 참조 타입 변환이 존재한다.

값 타입 변환의 예시는 int 타입의 변수를 float로 변환 할때, 변수의 값 유지를 위해서 데이터가 float 타입에 맞춰서 변환되는 것이다.

참조 타입 변환은 말 그대로 변수의 참조를 다른 타입의 참조로 할당하는 것이다. 예를 들어 int 타입의 변수를 float& 타입의 참조로 할당 하는 것이다.

Up Casting & Down Casting

타입 변환에는 변환이 진행 되는 두 타입의 크기에 따라 업 캐스팅, 다운 캐스팅으로 나뉜다.

업 캐스팅

업 캐스팅이란, 원본 변수의 타입의 크기보다 더 큰 타입으로 캐스팅 하는 것을 의미한다. 예를 들어 1바이트 크기의 char 타입의 변수를 2바이트 크기의 short 타입의 변수로 변환하는 변환이다.

이러한 업 캐스팅 변환은 캐스팅 시에 데이터가 잘리거나 변형이 일어나지 않기 때문에 안전한 캐스팅이라고 할 수 있다.

다운 캐스팅

다운 캐스팅이란, 원본 변수 타입의 크기보다 더 작은 타입으로 캐스팅 하는것을 의미한다. 예를 들어 short 형의 변수를 char 형의 변수로 변환하는 변환이다.

다운 캐스팅 시에는 원본 변수의 값이 변형되거나 잘릴수 있기 때문에 확실한 경우가 아니라면 불안전한 타입 변환에 속한다.

암시적 변환과 명시적 변환

타입 캐스팅은 명시적으로 캐스팅 하는 경우 말고도 컴파일러에 의해 안전하다고 판단 되면, 명시적으로 표시 하지 않아도 컴파일러가 자동으로 타입 캐스팅을 해주는 경우가 있다.

예를 들어, int -> float의 타입 변환은 업 캐스팅으로, 안전한 타입 변환이기 때문에 암시적 형변환이 가능하다.

따라서, 컴파일러는 메모리의 침범 가능성이 있는 형변환은 암시적으로 변환해주지 않는다.

클래스 객체간의 형변환

일반적으로 연관성이 없는 클래스 객체간의 형변환은 명시적으로 변환 하지 않는 한 불가능하다. 하지만 타입 변환 생성자 혹은 연산자를 정의해한다면 암시적으로도 가능하다.

만약 클래스가 부모 자식의 관계라면, 특정 상황에서는 형변환이 가능하다. 자식 클래스가 부모 클래스로 형변환을 하는 경우는 암시적 변환이 가능하다. 즉, 안전한 변환이라는 것이다.

하지만, 부모 클래스가 자식 클래스로 형변환을 하는 경우엔 암시적 형변환이 불가능하다.

이는 자식 클래스는 부모 클래스에 할당된 자원 요소들을 모두 포함하지만, 부모 클래스는 자식 클래스에 추가된 자원 요소들을 모두 포함 하지는 않기 때문이다.

포인터의 타입 변환

일반적으로 상속 관계의 객체의 포인터 간의 형변환은 매우 자주 사용된다. 하지만, 객체간의 형변환과 마찬가지로, 부모->자식의 형 변환은 명시적으로 해주어야하고, 자식->부모의 형변환은 암시적으로 가능하다.

class Player{
    // Player class
}

class Mage : public Player{
    // Mage
public:
    int _mana;
}

Player* player = new Mage(); // -> 가능하다
Mage* mage = player; // -> 불가능 하다

Mage* mage = (Mage*) player // -> 명시적 형변환으로 가능하다

위의 예시에서 Mage* mage = player가 불가능한 이유는, mage 포인터를 통해서 player 객체의 크기를 넘어서는 메모리 접근이 발생할 가능성이 있기 때문이다.

하지만, 위의 경우에서는 원본 객체를 Mage를 할당해서 Player 포인터로 관리하는 것이기 때문에 명시적 형변환을 통해서 변환하는 작업이 안전하다고 볼 수 있다.

형변환된 포인터의 소멸

Player* player = new Mage();
delete player;

위의 경우처럼 player 객체를 할당 해제 하게 된다면, 어떤 타입 객체의 소멸자가 호출될까?

이 해답은 간단하다. 컴파일러는 player 포인터가 Player 객체 타입이기 때문에, 아무리 Mage 타입으로 할당 했다고 해도 Player 객체의 소멸자를 호출 하게 된다. 이렇게 된다면, Player 크기 만큼의 객체만 할당 해제되고, Mage - Player 만큼의 메모리는 할당 해제 되지 않는 문제가 발생한다.

이와 같은 상황을 예방하기 위해서 최상위 부모 클래스의 소멸자에는 virtual 키워드를 붙여줘야 한다. virtual 키워드를 붙이는 순간, 해당 부모 클래스와 모든 자식 클래스에 vftable이 생성되고, Player 타입으로 Mage 객체가 소멸되더라도 vftable에 있는 Mage 소멸자를 제대로 호출 해서 올바른 객체 소멸을 구현할수 있다.

C++의 캐스팅 함수

C++에서는 타입 캐스팅을 해주는 네가지 함수가 구현되어 있다.

static_cast

static_cast는 가장 많이 사용되는 캐스팅 함수로, 일반적인 캐스팅 작업을 할 때 사용된다.

변수간의 업 캐스팅이나, 상속 관계에 있는 객체 간의 다운 캐스팅에도 사용된다.

dynamic_cast

dynamic_cast는 상속 관계의 객체간의 안전한 다운 캐스팅에 사용된다. 예를 들면, Mage 객체를 Player 포인터로 할당 한 후, 나중에 다시 Mage 객체를 사용하기 위해서 Player->Mage로 캐스팅 하는 경우에 사용된다.

dynamic_cast가 객체간 형변환을 안전하게 구현하는 방법은 RTTI라는 기법을 사용하여 이루어 진다.

RTTI란, Run Time Type Information의 약자로, virtual 함수를 구현하였을때 생성되는 vftable을 참고한 후, 원본 객체의 타입을 추론 한다. 이때, 변환하려는 타입이 원본 객체의 타입이라면, 형변환을 하고, 만약 다른 타입으로 형변환을 시도하면 nullptr을 반환한다.

위와 같은 방법으로 형변환이 구현되어 있기 때문에, dynamic_cast를 사용하기 위해선 반드시 객체에 하나 이상의 virtaul 함수가 구현되어 있어야 한다.

dynamic_cast는 안전하지만, 타입을 확인하는 과정이 포함 되기 때문에, static_cast보다 느리다는 단점이 있다.

const_cast

const_cast는 const 키워드가 붙은 타입에서 const를 제거할때 사용되는 함수이다. 일반적으로 잘 사용하진 않지만, const를 제거하는 경우, const를 제거하겠다는 명확한 의도를 나타내기 사용된다.

reinterpret_cast

reinterpret_cast는 가장 위험한 캐스팅 함수이다. 이 함수는 아무런 타입 검사도 시행하지 않고 강제로 캐스팅을 실행한다.

reinterpret_cast는 전혀 관련이 없는 두 포인터 혹은 변수 그리고 포인터->정수 변환 등에 사용한다.

profile
미국 공대생

0개의 댓글