C++의 캐스트(Cast) 연산자
C의 타입 캐스팅(형변환)은 명시적, 암시적으로 이뤄집니다.
int i = 3.5; int i = 0; double d = 3.5; i = (double)d;
암시적 캐스팅은 Data의 손실이 발생할 수 있기 때문에 문제가 될 수도 있습니다.
Visual C++에서는 int i = 3.5 데이터의 손실이 있을 수 있다는 다음과 같은 경고를 보여줍니다.
Warning C4244: 'initializing' : conversion from 'double' to 'int', possible loss of data
C에서의 타입 캐스팅은 너무나 유연해서 문제가 될 소지가 너무 많습니다.
int형 변수의 값을 주소값을 넣는 부분에 넣을 수도 있는데 컴파일러는 군말없이 그냥 다 형변환 해줍니다.
그래서 C++에서는 다음의 4가지 형식의 캐스트 연산자를 지원해줍니다.
static_cast
dynamic_cast
const_cast
reinterpret_cast
1. static_cast는 논리적으로 변경 가능한 경우에만 변경을 허용합니다.
사용 방식은 static_cast<변환할타입>(변환대상) 의 형태입니다.
static_int<int>(3.5); 이런식으로 사용이 가능합니다.
실수를 정수로, 정수를 실수로 변환하는 것은 가능하지만
포인터 타입을 정수 타입 등으로 변환하거나 하는 것은 방지해줍니다.
static_cast가 상속관계의 클래스에 있을 경우는 다음과 같이 적용이 됩니다.
부모 클래스 포인터가 부모 클래스를 가리키는 것은 가능.(당연히 가능)
자식 클래스 포인터가 자식 클래스를 가리키는 것도 가능.(당연히 가능)
부모 클래스 포인터가 자식 클래스를 가리키는 것도 가능.(다형성의 기초가 되는 중요한 내용이죠.)
자식 클래스 포인터가 부모 클래스를 가리키는 것도 가능.(위험하지만 허용이 됩니다.)
static_cast는 상속에서는 저 4가지 경우를 모두 허용합니다.
하지만 자식이 부모를 가리키는 상황에서 자식 클래스에만 있는 함수나, 데이터를 참조시에 위험합니다.
그래도 static_cast는 논리상 맞지 않는 변환을 막아주는 역할을 할 수 있습니다.
2. dynamic_cast는 실행시간 타입 정보(Runtime Type Information, RTTI)가 필요합니다.
이것은 Project 속성에서 C++ -> Language에 보시면 켤 수 있습니다.
Enable Run-Time Type Information이라는 옵션이 존재합니다.
포인터와 포인터간, 레퍼런스와 레퍼런스 간의 변환만을 허용합니다.
dynamic_cast와 static_cast의 차이는 자식 클래스 포인터가 부모 클래스를 받는 경우입니다.
dynamic_cast는 자식 클래스 포인터가 부모 클래스를 대입받을 수 있을 때는
부모 포인터가 가리키는 실체가 자식 객체일 때 입니다.
즉 자식 포인터 = 부모 포인터로 대입 받더라도 실제 부모 포인터가 가리키는 대상이 자식 객체이면
자식 포인터가 자식 객체를 가리키는 상황이 되므로 안전한 것이죠.
다음 코드를 확인하시면 이해가 쉬울 것 같습니다.
class Base { public: virtual ~Base(){}; }; class Derived : public Base { public: }; int main() { Derived derived; Base* pBase = &derived; Derived* pDerived = nullptr; pDerived = dynamic_cast<derived*>(pBase); return 0; }
일단 dynamic_cast를 사용하려면 Polymorphic type이어야 합니다.
그러기 위해서 소멸자를 virtual로 생성합니다.
pBase는 실제로는 자식 클래스의 객체인 derived를 가리킵니다.
이럴 경우에 pBase를 Derived 포인터로 변환이 가능한 것입니다.
3. const_cast는 상수 속성만 변경할 때 사용합니다.
volatile이나 __unaligned의 속성을 처리할 때도 사용 가능합니다.
상수성만 변경 가능하기 때문에 당연히 되는 형태의 형변환도 불가능합니다.
예를 들어 int를 double로 바꾸는 등의 형변환마저 불가능한 것입니다.
오직 상수성을 붙였다 뗐다 하는 역할만 하는 캐스트 연산자입니다.
4. reinterpret_cast는 C 스타일의 매우 위험한 변환이라고 할 수 있습니다.
처음 예로 들었던 정수값을 주소값으로 넣는다거나 하는 변환도 허용해 줍니다.
이 캐스트 연산자는 확실하게 변환이 가능한 상황에만 한정적으로 사용해야 합니다.
이것이 C++에서 사용 가능한 캐스트 연산자입니다.
아무래도 모양이 더 눈에 띄기 때문에 C에서 사용하는 명시적인 형변환보다 가독성이 좋습니다.
또한 꼭 필요한 범위의 형변환만 수행이 가능하기 때문에
되도록이면 C++ 스타일의 형변환을 사용하는 것이 안전합니다.