안전한 변환
char->short
, short->int
, int->__int64
아래 코드처럼, 업캐스팅한 기본 클래스의 포인터로는 기본 클래스의 멤버만 접근할 수 있다.
Point* pBase = pDer; // 업캐스팅
pBase->showColorPoint(); // ⛔️ 컴파일 오류 ⛔️ 기본클래스 Point 타입의 포인터 pBase로 파생 클래스인 ColorPoint 객체를 가리킨다. 하지만 pBase 는 Point 클래스의 포인터이므로, pBase 포인터로는 ColorPoint 객체 내 Point 클래스 멤버만 접근할 수 있다. showColorPoint() 함수는 Point 클래스의 멤버가 아니므로, 컴파일 오류가 발생한다.
Point* pBase = (Point*)pDer; // (Point*) 생략 가능
...파생 클래스의 포인터에 기본 클래스 타입의 주소가 치환되는 것을 다운 캐스팅이라고 한다.
static_cast
가 이에 해당한다int main(){
ColorPoint cp;
ColorPoint *pDer;
Point* pBase = &cp; // 💡업캐스팅
pBase->set(3,4);
pBase->showPoint();
pDer = (ColorPoint *)pBase; // 💡다운캐스팅
pDer->setColor("Red"); // 정상 컴파일
pDer->showColorPoint(); // 정상 컴파일
}
C++ 의 캐스팅에 대해 본격적으로 알아보자
1) static_cast ⭐️
2) dynamic_cast ⭐️
3) const_cast
4) reinterpret_cast
이러한 형변환 연산자들은 각각 다른 상황에서 사용되므로 올바른 형변환 연산자를 선택하여 코드를 작성해야 한다. 주의할 점은 reinterpret_cast
와 const_cast
는 잘못된 형변환을 수행할 수 있으므로 사용할 때 주의해야 한다.
타입 원칙에 비춰볼 때 상식적인 캐스팅만 허용해 준다.
float ratio = static_cast<float>(hp) / maxHp;
그러나 '상식적인' 이라는 의미가 항상 '옳다' 는 것은 아니다
Knight* k = new Knight();
Player* p = k; // 이건 '나이트 is a 플레이어' 너무 당연해서 언어 차원에서 암시적으로 허용
Knight* k2 = static_cast<Knight*>(p); // 원복시키고 싶을 때, 언어를 안심시켜준다
ItemType itemType = item->GetItemType();
if (itemType == IT_Weapon)
{
Weapon* weapon = static_cast<Weapon*>(item);
}
상속 관계에서의 안전 변환이다.
항상 되는 것은 아니며, 다형성 코드가 있어야 한다.
즉 하나라도 virtual
함수가 있어야 활용이 가능하다. (대표적으로 소멸자를 virtual
로 만드는 것)
왜 그럴까? 다른 캐스팅과 달리 RTTI(RunTime Type Information)
이기 때문이다.
가상함수를 하나라도 만들면 가상함수 테이블을 가리키는 공간이 생기고
가상함수테이블에는 함수주소들이 있고 이런게 동적 바인딩의 원리인데
이렇게 원본 타입을 확인해서 되는 경우에만 캐스팅을 해주고 그게 아니면 0
즉 nullptr
로 밀어버린다.
downcasting
과 같은 동적 형변환을 수행한다.
런타임에 타입을 확인하고 안전한 형변환이 아니면 nullptr
또는 예외를 반환한다.
class Base { virtual void foo() {} };
class Derived : public Base {};
Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // Base 포인터를 Derived 포인터로 형변환
Archer* k = new Archer();
Player* p = k;
Knight* k2 = dynamic_cast<Knight*>(p); // k2 에는 nullptr 가 들어있다
상수성(constness)
을 제거하거나 추가한다.
주로 포인터나 참조를 통해 사용되며, 상수성을 변경하여 값을 수정하거나 상수 객체를 비상수 객체로 형변환한다.
const int a = 10;
int* b = const_cast<int*>(&a); // 상수성을 제거하여 값을 수정할 수 있도록 함
const char* name = "SH";
char* name2 = const_cast<char*>(name);
포인터, 참조, 정수 간의 임의의 형변환을 수행한다.
안전하지 않은 형변환으로, 주로 저수준의 형식 변환에 사용된다고 한다.
int a = 10;
int* b = reinterpret_cast<int*>(a); // 정수를 포인터로 형변환
Dog* dog = reinterpret_cast<Dog*>(k);