IT/C++, MFC

16-1 : C++에서의 형 변환 연산 (static_cast: A 타입에서 B타입으로)

가성비몬 2023. 3. 6. 14:53
#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*)&num;		// 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++ 프로그래밍