C++ 진영에서는 C 스타일의 형 변환 연산자를 가리켜 ‘오래된 C 스타일 형 변환 연산자(Old C-style cast operator)’라 부르기도 한다. 이렇듯 C 스타일의 형변환 연산자는 C 언어와의 호환성을 위해서 존재할 뿐, C++에서는 새로운 형 변환 연산자와 규칙을 제공하고 있다.
C언어의 형 변환 연산자를 살충제와 비교하면, 이는 사람 잡는 약에 비유할 수 있다. 그만큼 강력해서 변환하지 못하는 대상이 없다는 뜻이다. 따라서 아래의 예제에서 보이는 실수를 해도 컴파일러는 이를 잡아내지 못한다.
class Car
...
class Truck : class Car
...
int main(void)
{
Car *parcar1 = new Truck(80, 200);
Truck *ptruck1 = (Truck *) pcar1; //문제없어 보이는 형 변환 ! 다형성 OK
ptruck1->ShowTruckState();
cout << endl;
Car *pcar2 = new Car(120);
Trucker *ptrucker2 = (Trucker *)pcar2; // 문제가 바로 보이는 형 변환! Car 객체의
// 다운캐스팅 ! NOT OK
ptruck2->ShowTruckerState();
return 0;
}
이러한 유형의 논란과 문제점 때문에 C++ 에서는 다음과 같이 총 4개의 연산자를 추가로 제공하면서 용도에 맞는 형 변환 연산자의 사용을 유도하고 있다.
dynamic_cast<T>(expr)
<> 에는 변환하고싶은 타입, 객체의 포인터 또는 참조형, () 사이에는 변환의 대상이 와야 한다. 요구한 형 변환이 적절하지 않은 경우에는 컴파일 시 에러가 발생한다. 여기서 말하는 적절한 형 변환은 다음의 경우를 뜻한다.
“상속관계에 놓여 있는 두 클래스 사이에서 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환하는 경우.”
Car *pcar1 = new Trucker(80, 200);
Truck *ptruck1 = dynamic_cast<Trucker *>(pcar1); // 컴파일 에러
Car *pcar2.= new Car(120);
Truck *ptruck2 = dynamic_cast<Truck*>(pcar2); // 컴파일 에러
Truck *ptruck3 = new Truck(70, 150);
Car *pcar3 = dynamic_cast<Car *>(ptruck3); // 컴파일 OK
static_cast<T>(expr);
static_cast 연산자를 사용하는 우리들에게 컴파일러는 다음과 같이 이야기한다.
“좋아! 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로뿐만 아니라, 기초 클래스의 포인터 및 참조형 데이터도 유도 클래스의 포인터 및 참조형 데이터로 아무런 조건없이 형 변환시켜 줄게, 하지만 그에 대한 책임은 네가 져야 해!”
Truck *ptruck2 = static_cast<Truck *>(pcar2);
pcar2 가 가리키는 대상이 Car 객체이기 떄문에 이러한 형태로 문장을 구성하면 안 된다.
“static_cast 연산자는 dynamic_cast 연산자와 달리, 보다 많은 형 변환을 허용합니다. 하지만 그에 따른 책임도 프로그래머가 져야 하기 때문에 신중하게 선택해야 합니다. dynamic_cast 연산자를 사용할 수 있는 경우에는 dynamic_cast 연산자를 사용해서 안전성을 높여야 하며, 그 이외의 경우에는 정말 책임질 수 있는 상황에서만 제한적으로 static_cast 연산자를 사용해야 합니다.”
그리고 static_cast 연산자는 기본 자료형 데이터간의 형 변환에도 사용이 된다.
int main(void)
{
int num1=20, num2=3;
double result = 20/3;
cout << result << endl;
....
}