16-1 : C++에서의 형 변환 연산 (static_cast: A 타입에서 B타입으로)
#include <iostream>
using namespace std;
class Car
{
private:
int fuelGauge;
public:
Car(int fuel) : fuelGauge(fuel)
{ }
void ShowCarState() { cout << "잔여 연료량: " << fuelGauge << endl; }
};
class Truck : public Car
{
private:
int freightWeight;
public:
Truck(int fuel, int weight) : Car(fuel), freightWeight(weight)
{}
void ShowTruckState()
{
ShowCarState();
cout << "화물의 무게: " << freightWeight << endl;
}
};
int main(void)
{
Car * pcar1 = new Truck(80, 120);
Truck* ptruck1 = (Truck*)pcar1; // 문제 없어 보이는 형 변환!
ptruck1->ShowTruckState();
cout << endl;
Car* pcar2 = new Car(120);
Truck * ptruck2 = (Truck *)pcar2;
ptruck2->ShowTruckState();
return 0;
}
static_cast 형 변환 연산자는 다음의 형태를 갖는다. (dynamic_cast 연산자와 동일한 형태이다.)
static_cast<T>(expr)
"좋아! 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터뿐만 아니라, 기초 클래스의 포인터 및 참조형 데이터도 유도 클래스의 포인터 및 참조형 데이터로 아무런 조건 없이 형 변환 시켜줄게, 하지만 그에 대한 책임은 네가 져야 해!"
#include <iostream>
using namespace std;
class Car
{
private:
int fuelGauge;
public:
Car(int fuel) : fuelGauge(fuel)
{ }
void ShowCarState() { cout << "잔여 연료량: " << fuelGauge << endl; }
};
class Truck : public Car
{
private:
int freightWeight;
public:
Truck(int fuel, int weight) : Car(fuel), freightWeight(weight)
{}
void ShowTruckState()
{
ShowCarState();
cout << "화물의 무게: " << freightWeight << endl;
}
};
int main(void)
{
Car * pcar1 = new Truck(80, 120);
Truck* ptruck1 = static_cast<Truck*>(pcar1); // 컴파일 OK!
ptruck1->ShowTruckState();
cout << endl;
Car* pcar2 = new Car(120);
Truck * ptruck2 = static_cast<Truck *>(pcar2); // 컴파일 OK! 그러나!
ptruck2->ShowTruckState();
return 0;
}
.Truck * ptruck1 = static_cast<Truck*>(pcar1);
"포인터 pcar1을 Truck의 포인터 형으로 변환 시키겠습니다. 이건 제가 의도한 것이고요. 따라서 그에 대한 책임도 제가 지겠습니다."
.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;
}
정수형 나눗셈의 결과로 6이 출력되기 때문에 실수형 나눗셈을 진행하려면
double result = (double) 20/3;
또는
double result = (double) (20) /3;
C++에서는 static_cast 연산자를 이용한 다음의 문장구성을 더 추천한다.
double result = static_cast<double>(20)/3;
static_cast 연산자는 '기본 자료형 간의 형 변환'과 '클래스의 상속관계에서의 형 변환'만 허용을 하지만, C언어의 형 변환 연산자는 다음과 같이 말도 안되는(어쨌든 일반적이지 않은) 형 변환도 허용하기 때문에 static_cast 연산자의 사용은 의미를 갖는다.
int main(void)
{
const int num=20;
int * ptr=(int*)# // const 상수의 포인터는 const 포인터이다!
*ptr=30; // const 상수 num의 값이 실제로 변경된다.
cout<<*ptr<<endl; // 30이 출력된다.
float * adr=(float*)ptr; // int형 포인터를 float형으로 변환한다.
cout<<*adr<<endl;; // 저장된 데이터를 float형으로 해석해서 출력한다.
위에서 보인 형 변환은 static_cast 연산자로는 불가능한 형 변환이다. 즉, 여전히 static_cast 연산자는 C언어의 형 변환 연산자보다는 적은 것을 허용하고 있으며, 이로 인해서 static_cast 연산자를 보는 순간 다음과 같이 판단할 수 있다.
"흠, 상속관계에 있는 클래스의 포인터 및 참조형 데이터의 형 변환인가? 아니면 기본 자료형 데이터의 형 변환인가?"
참고문헌
윤성우, 오렌지미디어, 열혈 C++ 프로그래밍