C++방식의 캐스팅 4종류를 알아볼 것이다.
static_cast
dynamic_cast
const_cast
reinterpert_cast
C스타일의 캐스팅 코드는 동일한 코드여도 어디서 사용하느냐에 따라서 완전히 의미가 달라지는 것이 안 좋다.
그래서 우리가 사용할 때 우리가 어떤 방식으로 사용하는지 명시하기 위해 사용한다고 보면 된다.
static_cast
는 타입 원칙에 비춰볼 때 상식적인 캐스팅만 허용해준다.
대표적으로 보면 int
를 float
로 캐스팅 해주는 경우가 있다.
int hp = 100;
int maxHp = 200;
float ratio = hp / maxHp;
여기서 ratio는 당연하게 0이다.
왜냐하면 hp와 maxHp의 계산인데 둘 다 int
형이기 때문에 0으로 계산되고 들어가서 0.0000..으로 나타나는 것이다.
애를 float
로 바꿔주기 위해서는 C에서는
float ratio = (float)hp / maxHp;
이런식으로 캐스팅
을 하였다.
이런식이면 두번째 값이 int
라 하여도 float
로 계산이 되기 때문에 제대로 값이 나온다.
그리고 이렇게 타입 원칙에 비춰볼 때 상식적인 상황! 일떄
float ratio = static_cast(hp) / maxHp;
static_cast
를 이런식으로 사용할 수 있다.
2번째 경우를 보자면
부모와 자식간의 상속관계에 쓰는 캐스팅 같은 거를 할 때
예를 들어 A라는 클래스와 B라는 클래스가 있을 때 B라는 클래스가 A클래스를 상속 하고 있을 때
B* b = new B();
A* a = b; // B는 A이기 때문에 암시적으로 형변환이 된다.
B* b2 = a; // A는 B가 아니기에 당연하지 않기 때문에 암시적으로 형변환이 되지 않는다.
그럴 때 (B*)a로 캐스팅을 해주었는데
이럴 때 static_cast
를 사용할 수 있다.
B* b2 = static_cast<B*>(a);
하지만 이 static_cast
는 C스타일에 ()캐스팅을 그냥 C++버전으로 넘겨준 것과 같기 때문에 잘못 사용하면 메모리누수가 일어날 수 있다.
상속 관계
에서의 안전 변환
다형성을 활용하는 방식이기에 virtual
이 하나라도 있어야 사용가능하다.
어차피 클래스의 소멸자
를 virtual
로 해주는 것이 좋기때문에 소멸자
에 virtual
을 붙여넣겠다.
그럼면 바로 이 코드가 작동을 한다.
B* b2 = dynamic_cast<B*>(a);
왜 꼭 virtual함
수가 있어야 하냐고 물어본다면
이 dynamic_cast
는 RTTI
로 작동된다.
RTTI(RunTime Type Infomation)
인데
가상함수
를 하나라도 만들게 된다면 객체의 첫번째 공간에는 가상함수 테이블을 가르키는 공간(가상 함수 포인터?)가 만들어 진다.
이 가상함수 테이블
에는 자신이 호출해야할 테이블 주소가 기입이 된다.
그 테이블에는 우리가 객체를 만들었을 때 어떤 함수를 실행해야되는지에 대한 정보가 기입이 되어 첫번째 정보를 보고 판단할 수 있다.
그게 바로 동적 바인딩
ui의 원리였다.
그래서 dynamic_cast
같은 경우 원본객체가 a타입인지 아닌지에 따라 캐스팅을 성공시켜줄 수도 있고 nullptr로 밀어버릴 수도 있다.
B* b2 = static_cast<B*>(a);
이런식으로 하면 캐스팅 시켜주지만
A를 상속받은 C클래스로 선언했을 때
C* b = new C();
A* a = b;
B* b2 = static_cast<B*>(a);
이러한 경우에는 nullptr로 밀어준다.
그래서 원본 객체가 무엇인지 찾을 때 dynamic_cast
를 통해서
if (b2 != nullptr)
{
// B가 맞네
}
이런식으로 판별
도 해줄 수 있다. (C#의 as와 비슷하다고 한다.)
성능상으로 dynamic_cast
가 좀 느리다고는 하는데
뭐 이정도 기능이면 사용하는게 더 편할 것 같다.
정리해보자면 dynamic_cast
를 사용하면 상속관계에서의 안전한 변환을 해줄 수 있다.
그리고 가상함수 정보를 보고 판별하는 것이기 때문에(RTTI)
반드시 가상함수를 넣어줘야된다!
일단 먼저 말하자면 거의 사용할 일이 없다..
const
를 강제로 때어주는 역활을 한다.
const char* name = "200won";
char* name2 = const_cast<char*>(name);
이런식으로 const
를 빼준다.
근데 애당초에 const
를 빼줄꺼였으면 상수처리를 하면 안되는 것 같다..
위험하고
,강력하다
.
포인터를 전혀 관계없는 다른 타입으로 변화할 때 사용한다.
예시를 들자면 A클래스
가 있는데 전혀 관계없는 D클래스
로 변환할 때
A* a = new A();
D* d = (D*)a;
이런식으로 C스타일 캐스팅으로 사용했다.
이거를
A* a = new A();
D* d = reinterpret<D*>(a);
이런식으로 사용한다.
근데 이런식으로 사용할 일이 있나? 싶을 수도 있다.
뭐 생길 수도 있다.
void* p = malloc(1000);
D* d = reinterpret<D*>(a);
이런식으로 사용 될 수도 있고
__int64 address = reinterpret<__int64>(k);
이런식으로 주소값을 받아서 쓰는 경우도 있다고 하는데
아직까지는 잘 모르겠다.
하지만 static_cast
나 dynamic_cast
보다 사용빈도가 높지 않기 때문에
이런 것이 있다하고 기억만 하고 있으면 될 것 같다.
static_cast
와 dynamic_cast
가 주로 많이 쓰인다.
const_cast
와 reinterpret_cast
는 잘 쓰이지 않는다.
상속구조에서 dynamic_cast
를 사용하면 더 좋은 처리를 할 수 있다.
여기서 하나 알 수 있는 것은 C스타일에서는 모두 같은 문법을 사용하여 캐스팅하였지만 C++에서는 사유별로 다른 캐스팅 문법을 써주는 것을 알 수 있다.
즉 명시적
으로 나타내준다! 라고도 할 수 있다.