[C++] C++의 형변환
REF : 윤성우의 열혈 C++
C++의 형변환 연산
- C++에선 기존 C스타일의 형변환을 Old C-style 이라고 부른다, 단순히 호환성을 위해 존재한다.
- 기존 C스타일의 형변환은
형변환의 목적을 한 눈에 알아볼 수 없다.
는 문제점이 존재한다. - 코드를 읽는사람으로 하여금 해당 형변환이 어떤 의도로 된것인지 쉽게 예측할 수 없기때문에 그렇다.
- 이러한 논란과 문제점으로 인해 C++만의 형변환 연산자가 추가되었다.
- C++만의 형변환 연산자와 규칙을 알아보자.
1. dynamic_cast
- 상속관계에서의 안전한 형 변환을 지원한다.
유도클래스의 포인터와 참조형 데이터
를기초 클래스의 포인터 및 참조형 데이터로
형변환하는 경우(업캐스팅)#include <iostream> using namespace std; class Base { public: void VFnc(void) { cout<<"This is Base"<<endl; } }; class Derived : public Base { public: void VFnc(void) { cout<<"This is Derived!"<<endl; } }; int main(void) { Derived *ptr2 = new Derived; /*dynamic_cast upcasting*/ Base *base1 = dynamic_cast<Base *>(ptr2); base1->VFnc(); //"This is Base" }
- Dynamic Cast로 업캐스팅을 하게되면, 기초클래스의 메서드를 호출하게된다.
- 물론, 이때도 VFnc가 virtual로 선언되어있다면, 포인터형을보고 호출하는게 아닌, 내부 객체의 가상함수테이블에서 함수를 호출하므로, 유도클래스의 함수를 호출하게된다.
기초 클래스가 Polymorphic 클래스(가상함수가 있는 클래스)
일때,유도클래스의 포인터 및 참조형 데이터로
변환(다운캐스팅)class Base { public: virtual void VFnc(void) { cout<<"This is Base"<<endl; } }; class Derived : public Base { public: void VFnc(void) { cout<<"This is Derived!"<<endl; } }; int main(void) { Base *ptr1 = new Derived; /*dynamic_cast downcasting*/ Derived *derive1 = dynamic_cast<Derived *>(ptr1); }
- Base클래스가 가상함수를 지닌
Polymorphic 클래스
이므로, Derived 클래스도Polymorphic
이다. - 이런 Polymorphic 클래스를 업캐스팅하게되면 유도클래스의 포인터로 형변환할 수 있다.
- Base클래스가 가상함수를 지닌
2. static_cast
- dynamic_cast보다 많은 형 변환을 허용한다.
- 다운캐스팅, 업캐스팅을 자유롭게 사용할 수 있다.
- 다양한 형태의 형변환을 허용하는데, 안정성을 위해서는
dynamic_cast
를 사용하고, 제한적인 상황에서만static_cast
를 사용해야한다.
#include <iostream>
using namespace std;
class Base
{
public:
void VFnc(void)
{
cout<<"This is Base"<<endl;
}
};
class Derived : public Base
{
public:
void VFnc(void)
{
cout<<"This is Derived!"<<endl;
}
};
int main(void)
{
Base *base1 = new Derived;
Derived *derive = static_cast<Derived *>(base1);
derive->VFnc();
Base *base2 = new Base;
Derived *error = static_cast<Derived *>(base2);
error->VFnc(); //"This is Derived!"
}
- 실제 내부 객체가 Derived인 포인터를 Derived로 형변환하는 것은 논리적으로 정당화될 수 있다.
- 하지만, 내부 객체가 Base인 포인터를 Derived로 바꾸는 것은 정당화될 수 없다.
- 실제로 현재
error
라는 포인터는 static_cast로는 컴파일 에러를 발생시키지 않는다. - 가상함수 테이블이 없으므로, 포인터형만으로 함수를 호출하게되는데, Derived 포인터이므로, Derived클래스의 VFnc를 호출하게된다.
- 실제 내부 객체가 base임에도 불구하고, 단순히 포인터형에 의해 이를 호출하는 것은 논리적으로 말이 안된다.
- 하지만, dynamic cast로 형변환을 하게되면 현재 Base가
Polymorphic
하지 않으므로, 컴파일에러를 발생시킨다. - 이를 막기위해 Base클래스의 메서드 중 하나를 virtual로 선언해주면 되긴하나,
dynamic_cast 연산자는 형변환하고자하는 대상 객체가 아닐경우, NULL을 반환한다.
- 만약 참조형일 경우,
bad_cast exception
이 발생한다.
#include <iostream> using namespace std; class Base { public: virtual void VFnc(void) { cout<<"This is Base"<<endl; } }; class Derived : public Base { public: void VFnc(void) { cout<<"This is Derived!"<<endl; } }; int main(void) { Base *base2 = new Base; Derived *error = dynamic_cast<Derived *>(base2); //Null 반환 if (!error) return 0; }
- dynamic_cast는 컴파일시간이 아닌, 프로그램이 실행될때의 안정성을 검사하기때문에, static_cast보다 실행속도는 느리지만 안정적인 형변환이 가능하다.
- 이러한 기능을 RTTI라고하며, dynamic_cast와 RTTI를 참고하자
- 이렇듯, static_cast보다 dynamic_cast가 보다 안전한 형변환을 할 수 있으므로, 제한적이 상황에서만 static_cast를 사용해야한다.
- 만약 참조형일 경우,
- 다만, static_cast가 유용하게 사용되는 경우가 있는데,
기본 자료형 간의 형 변환
이다. - int, float, double 등 데이터의 형변환을 할 때 사용되는데, static_cast가 C의 형변환과 다른 점은
포인터간의 형변환을 허용하지 않는다.
는 점이다.
#include <iostream>
int main(void)
{
int num = 0;
int *p1 = #
float *e1 = static_cast<float *>(p1);//컴파일 에러
float *e2 = (float *)p1;
return 0;
}
- 실제로, 이 코드를 실행해보면, 포인터를 다른 포인터로 형변환하는 행위는 불가능하다.
- 이렇게 static_cast가
기본 자료형간 형변환
,상속관계의 클래스와 포인터의 형변환
만을 허용하므로, 프로그래머는 이 둘 중 하나를 파악할 수 있다.
3. const_cast
- const 성향을 삭제하는 캐스팅
- const의 성향을 삭제하는 것이 const의 가치를 떨어뜨린다고 생각할수도 있지만, 나름의 이유가 있다.
- 함수의 인자 전달 시, const 선언으로 인한 형불일치를 수정해준다.
- 라이브러리 함수를 사용할때, const형이 맞지 않을 때 제한적으로 사용하는 것이 좋다.
4. reinterpret_cast
- 상관없는 자료형으로의 형변환
#include <iostream>
using namespace std;
int main(void)
{
int num = 1;
char *ptr = reinterpret_cast<char *>(&num);
for (int i = 0; i < sizeof(num); i++)
{
cout<<static_cast<int>(*(ptr + i))<<endl;
}
return 0;
}
- 정수자료형 int가 4바이트로 숫자를 표현한다.
- 이를 1바이트단위로 접근할 수 있도록 char *로 변환해서 1바이트씩 값을 읽어온다.
- 이러한 reinterpret_cast의 사용은 주소값 출력에도 이용될 수 있다.
- C에서는 포인터의 주소를 출력하기위해서는 포인터의 형을 long long으로 형변환했어야했는데, reinterpret캐스팅로 이것이 가능하다.
댓글남기기