이번에는 C++언어에서 제공하는 형 변환에 관해 살펴보기로 해요.
이 부분은 OOP 특징은 아니지만 앞에서 dynamic_cast를 사용하는 방법을 소개하여 다른 형 변환도 알아보려는 거예요. 먼저 C++ 언어에서도 강제 형변환(명시적 형변환이라고도 부름)을 제공하고 있습니다. 하지만 강제 형변환은 잘못 사용하면 심각한 버그를 유발할 수 있습니다.
다음은 서로 관련이 없는 Man 클래스와 Student 클래스를 정의한 후에 강제 형변환을 사용하는 예제입니다. 컴파일 오류는 발생하지 않지만 프로그램 동작 중에 버그로 런 타임 오류가 발생할 수 있습니다.
//강제 형 변환이 갖는 위험 요소 #include <iostream> #include <string> using namespace std; class Man { string name; public: Man(string name) { this->name = name; } void View()const { cout<<"이름:"<<name<<endl; } }; class Student { int num; string name; public: Student(int num, string name) { this->num = num; this->name = name; } void View()const { cout<<"번호:"<<num<<"이름:"<<name<<endl; } }; int main() { Student *stu = new Student(30,"홍길동"); Man *man = (Man *)stu; //강제 형변환 man->View(); delete stu; return 0; }
C++에서는 보다 엄격하게 형 변환할 수 있는 방법을 제공하고 있습니다. 앞에서 다루었던 dynamic_cast는 일반화 관계에서 하향 캐스팅을 제공했었죠. 이 외에 static_cast, const_cast, reinterpret_cast를 제공하고 있습니다.
먼저 static_cast는 C언어의 강제 형변환과 유사합니다. 기반 클래스와 파생 클래스 사이의 형 변환 뿐만 아니라 기본 자료 형식 사이에서도 형 변환을 할 수 있습니다. 하지만 이에 관한 모든 책임은 개발자에게 있다는 것을 잊지 마세요.
다음은 하향 캐스팅을 dynamic_cast를 이용하지 않고 static_cast를 이용하였습니다. dynamic_cast를 사용하려면 가상 메서드를 갖고 있어야 하지만 static_cast는 가상 메서드를 갖고 있지 않아도 가능합니다.
//static_cast #include <iostream> #include <string> using namespace std; class Man { string name; public: Man(string name) { this->name = name; } void View()const { cout<<"이름:"<<name<<endl; } }; class Student:public Man { int num; string name; public: Student(int num, string name):Man(name) { this->num = num; this->name = name; } void View()const { cout<<"번호:"<<num<<"이름:"<<name<<endl; } void Study() { cout<<name<<" 공부하다."<<endl; } }; int main() { Man *man = new Student(30,"홍길동"); Student *stu = static_cast<Student *>(man); stu->Study(); delete man; return 0; }
▷ 실행 결과
홍길동 공부하다.
다음은 기본 형식 사이에 static_cast를 사용한 예제입니다.
//기본 형식 간에 static_cast #include <iostream> using namespace std; int main() { int a = 3; double d; d = static_cast<double>(a); cout<<d<<endl; return 0; }
▷ 실행 결과
3
하지만 언제나 static_cast를 할 수는 없습니다. 예를 들어 string 형식과 int 형식 사이에서는 static_cast를 할 수 없습니다.
//static_cast 를 할 수 없는 예 #include <iostream> #include <string> using namespace std; int main() { int a = 3; string name; name = static_cast<string>(a);//형 변환 오류 cout<<name<<endl; return 0; }
static_cast는 다양한 형태에서 형 변환을 할 수 있지만 모든 책임은 개발자의 몫임을 항상 잊지 마세요.
이번에는 const_cast를 알아봅시다.
const 포인터 변수는 가리키는 값을 변경할 수 없죠. const_cast는 const 포인터 변수가 가리키는 값을 변경할 수 있게 형 변환합니다. 만약 값을 변경하지 않겠다는 의미로 const 포인터 변수를 입력 인자로 받은 후에 const_cast로 값을 변경할 수 있다는 것입니다. 이러한 사용은 프로그램의 신뢰성이 떨어질 수 있습니다.
//const_cast #include <iostream> using namespace std; void View(const int *input) { int *temp = const_cast<int *>(input); *temp = 9; cout<<"=== 가리키는 값 출력 ==="<<endl; cout<<"*temp:"<<*temp<<endl; cout<<"*input:"<<*input<<endl; } int main() { int value = 10; View(&value); return 0; }
▷ 실행 결과
=== 가리키는 값 출력 ===
*temp:9
*input:9
마지막으로 reinterpret_cast를 알아봅시다. reinterpret_cast는 강제 형변환처럼 거의 모든 형 변환을 가능하게 해 줍니다. 예를 들어 flat * 를 int *로 형 변환을 요청하는 것을 허용합니다. 이러한 변환에서는 형 변환한 곳의 값을 float 형식이 아닌 int이라고 해석하여 값을 출력합니다.
다음은 float 형식 변수 -12.625가 메모리에 어떻게 기록하는지 확인하기 위해 reinterpret_cast를 사용한 예제 코드입니다.
//reinterpret_cast #include <iostream> #include <iomanip> #include <string> using namespace std; int main() { float f=-12.625; float *pf = &f; int *a; a = reinterpret_cast<int *>(pf); cout<<hex<<(*a)<<endl; return 0; }
▷ 실행 결과
c14a0000
dynamic_cast는 다형성이 갖는 단점을 해결하기 위해 사용하지만 다른 형 변환은 극약 처방이 필요한 응급 상황이 아니면 사용을 자제해 주세요.