PowerfullCasting.cpp
#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, 200);
Truck* ptruck1 = (Truck*)pcar1; // 문제 없어 보이는 형 반환
ptruck1->ShowTruckState();
cout << endl;
Car* pcar2 = new Car(120);
Truck* ptruck2 = (Truck*)pcar2; // 문제가 바로보이는 형 반환
ptruck2->ShowTruckState();
return 0;
}
기초 클래스의 포인터 형을 유도 클래스의 포인터 형으로 형 변환하는것은 일반적인 경우의 형 변환은 아니다. 또한 문제가 바로 보이는 형 반환과 같이 저러한 형 변환 연산은 문제가 되지만 위는 C 스타일의 형 변환 연산자인 '무적의 형 변환 연산자'여서 변환이 가능하게 해준다.
하지만 그 결과, 화물의 무게를 출력하는 부분에 있어서 의미없는 값이 들어가게 된다.
C++ 에서는 총 4개의 형변환 연산자를 제공한다.
다이내믹 캐스트의 경우 상속관계에 놓여 있는 두 클래스 사이에서 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환하는 경우 사용한다.
DynamicCasting.cpp
#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, 200);
Truck* ptruck1 = dynamic_cast<Truck*>(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!
return 0;
}
기초클래스 : Car, 유도클래스 : Truck인 상황에서 Truck의 포인터 및 참조형 데이터를 Car의 포인터 및 참조형 데이터로 형 반환해주는 모습을 보여준다.
static_cast는 dynamic_cast의 특성 뿐만아니라 기초클래스의 포인터 및 참조형 데이터도 유도 클래스의 포인터 및 참조형 데이터로 아무런 조건없이 형 변환시켜준다. 그러나 이에 대한 책임은 프로그래머가 져야 한다.
StaticCasting.cpp
#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, 200);
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;
}
static_cast 연산자는 dynamic_cast 연산자와 달리, 보다 많은 형 변환을 허용한다. dynamic_cast 연산자를 사용할 수 있는 경우에는 dynamic_cast연산자를 사용해서 안전성을 높여야 하며, 그 이외의 경우에는 정말 책임질 수 있는 상황에서만 제한적으로 static_cast 연산자를 사용해야한다.
일반적으로 static_cast 연산자는 C언어의 형변환 연산자보다 더 적은 범위의 변환을 허용하기 때문에 static_cast를 주로 이용하는 것이 안전함 (C언어의 일반적인 형 변환 연산자보다)
ConstCasting.cpp
#include <iostream>
using namespace std;
void ShowString(char* str)
{
cout << str << endl;
}
void ShowAddResult(int& n1, int& n2)
{
cout << n1 + n2 << endl;
}
int main(void)
{
const char* name = "Lee Sung Ju";
ShowString(const_cast<char*>(name));
const int& num1 = 100;
const int& num2 = 200;
ShowAddResult(const_cast<int&>(num1), const_cast<int&>(num2));
return 0;
}
const로 선언된 char(별) 변수를 매개변수가 char(별) 로 받는 함수에 넣기위해 const_cast를 사용하였다. const int또한 마찬가지 const_cast를 사용하여 인자전달.
포인터를 대상으로 하는, 그리고 포인터와 관련이 있는 모든 유형의 형 변환을 허용한다.
ReinterpretCasting.cpp
#include <iostream>
using namespace std;
int main(void)
{
int num = 0x010203;
char* ptr = reinterpret_cast<char*>(&num);
for (int i = 0; i < sizeof(num); i++)
cout << static_cast<int>(*(ptr + i)) << endl;
return 0;
}
int형 정수에 바이트 단위 접근을 위해서 int형 포인터를 char형 포인터로 형 반환하고 있다.
dynamic_cast또한 static_cast처럼 기초클래스의 포인터 및 참조형 데이터를 유도클래스의 포인터 및 참조형 데이터로 형 변환이 가능하다. 단, 기초클래스가 Polymorphic 클래스인 경우에만!
Polymorphic 클래스랑 하나 이상의 가상함수를 지니는 클래스를 뜻한다.
PolymorphicDynamicCasting.cpp
#include <iostream>
using namespace std;
class SoSimple // Polymorphic 클래스! ShowSimpleInfo가 가상함수 이므로
{
public:
virtual void ShowSimpleInfo()
{
cout << "SoSimple Base Class" << endl;
}
};
class SoComplex : public SoSimple
{
public:
void ShowSimpleInfo() // 이것 역시 가상함수!
{
cout << "SoComplex Derived Class" << endl;
}
};
int main(void)
{
SoSimple* sim = new SoComplex;
SoComplex* comPtr = dynamic_cast<SoComplex*>(sim);
comPtr->ShowSimpleInfo();
return 0;
}
포인터 변수 sim이 실제로 가리키는 객체가 SoComplex이기 때문에 가상함수를 사용하였을 시 dynamic_cast로 변환을 하여도 문제가 되지 않는다! 하지만 위에서 new SoComplex가 아닌 new SoSimple이었다면 형변환을 하지 못하고 NULL 값을 반환한다.
dynamic_cast는 안정적인 형 변환을 보장하며, 컴파일 시간이 아닌 실행시간에 안전성을 검사하도록 컴파일러가 바이너리 코드를 생성한다.
dynamic_cast 연산자를 이용한 형 변환의 과정에서 발생할 수 있는 예외는 bad_cast이며 what()함수를 이용해 예외의 원인을 표현할 수 있다. 이러한 예외가 발생할 수 있기 때문에 반드시 이에대한 예외처리를 해야한다고 한다.
이렇게 '윤성우의 열혈 C++ 프로그래밍' 책이 끝났다. C++을 공부하면서 C언어를 하면서 몰랐던 부분이나 이해가 되지 않았던 부분도 많이 공부하면서 알게되어 좋았고 맺는말을 읽으면서 앞으로 어떠한 것을 공부하면 좋을 지, 공부할 때 어떤 마음가짐을 가지는게 좋은 지 알게 되었다. 앞으로 C++을 많이 사용하게 될지는 모르겠지만 주로 PS를 할때 C++을 사용하면서 많이 익숙해지게 노력해야겠다.