CPP에서는 안전한 casting을 보장하기 위해 casting을 도와주는 4가지 타입 변환 연산자가 있다.
( static_cast, dynamic_cast, const_cast, reinterpret_cast )
1. static_cast
- 실수와 정수, 열거형과 정수형, 실수와 실수 캐스팅 가능
- 컴파일 타임에 형 변환을 하기 때문에 컴파일 당시 오류를 확인할 수 있는 장점이 있다.
static_cast <바꾸려는 타입>(바꿀 대상)
밑과 같이 부모를 private, protected로 상속받을 경우에는 캐스팅이 되지 않음!
class A {
};
class B : A {
};
int main() {
A aa;
B bb;
// 둘 다 불가능
aa = bb;
aa = static_cast<A>(bb);
}
부모를 public으로 상속받을 경우에만 캐스팅이 됨!
static_cast를 사용하지 않아도 컴파일러가 자동으로 만든 부모 class의 할당 연산자를 통해 자동으로 자식 -> 부모 캐스팅해서 값이 들어가게 된다.
class A {
};
class B : public A {
};
int main() {
A aa;
B bb;
// 둘 다 가능. 하지만 static_cast로 하는게 당연히 더 안전함.
aa = bb;
aa = static_cast<A>(bb);
}
static_cast는 업 캐스팅은 가능하지만, 다운 캐스팅은 막아준다!
클래스 포인터의 경우에는 둘 다 가능! <- 밑에 자세히 나온다.
class A {
};
class B : public A {
};
int main(void) {
A aa;
B bb;
// 가능
aa = static_cast<A>(bb);
// 불가능
bb = static_cast<B>(aa);
}
static_cast는 function -> function pointer, array -> array pointer의 캐스팅은 지원한다.
pointer를 무언가로 캐스팅하는 것은 지원하지 않는다.
하지만 클래스의 pointer는 예외적으로 가능하다.
다운 캐스팅 업 캐스팅 둘 다 컴파일 오류가 나지는 않는다. 하지만 다운 캐스팅 같은 경우 안전이 보장되지 않는다.
class A {
};
class B : public A {
};
int main(void) {
int *a = new int[4];
// 불가능
int b;
b = static_cast<int *>(a);
// 가능
A *aClass;
B *bClass;
aClass = static_cast<A *>(bClass);
bClass = static_cast<B *>(aClass);
}
2. dynamic_cast
위의 static_cast에서 클래스의 포인터로 캐스팅을 했을 때 다운 캐스팅의 안정성을 보장받을 수 없었다.
이를 해결하기 위해(?) dynamic_cast가 존재한다.
dynamic_cast는 RTTI( Runtime Type Information )을 통해 다운 캐스팅을 할 수 있게 해 준다.
다운 캐스팅이 항상 위험한 것은 아니다
밑의 예제와 같은 상황이 있다고 보자.
자식 -> 부모 -> 자식
(업 캐스팅)(다운 캐스팅)
자식에서 부모로 업 캐스팅을 하고 다시 자식으로 다운 캐스팅을 하는 경우는 위험하지 않다.
RTTI는 실행 중 포인터가 가리키는 객체의 타입을 알 수 있게 해 준다.
RTTI를 참조하는 주소를 가상 테이블 vtable의 첫 번째 요소인 type_info에 저장한다.
한마디로 모든 클래스마다 RTTI를 사용할 수 있는 것이 아닌 virtual function이 있는 클래스
즉, 다형성을 가진 클래스만 사용할 수 있다.
class A {
// public:
// virtual void hi() {
// std::cout << "hi" << std::endl;
// }
};
class B : public A {
};
int main(void) {
A * a;
B * b;
a = dynamic_cast<A *>(b);
// 오류
b = dynamic_cast<B *>(a);
//오류
b = dynamic_cast<B *>(a);
}
위의 코드와 같이 A 클래스의 가상 함수를 없애면 다운 캐스팅과 (자식 -> 부모 -> 자식)의 다운 캐스팅이 안된다.
A 클래스의 virtual 함수를 만들면 다운 캐스팅이 가능해진다.
dynamic_cast는 reference, pointer 두 가지의 타입만 허용한다.
reference에서 타입 오류가 났을 때는 bad_cast 예외를 throw 한다.
pointer에서 타입 오류가 났을 때는 NULL를 리턴한다. bad_cast 예외를 throw 한다.
3. const_cast
const_cast이 const로 선언된 상수의 값을 변경해주는 것이라고 착각한다.하지만 밑의 코드와 같이 실제로 const의 값을 변경해보려고 하면 오류가 난다.
int main(void) {
const int a = 4;
a = *const_cast<int *>(a);
}
const_cast는 상수의 값을 변경하는 것이 아니라 const로 선언하지 않는 변수를 가리키는 pointer나 reference에 대해 const의 속성을 없애 값을 대입할 수 있게 해 준다.
밑의 코드에서 주석 처리된 코드는 오류가 난다. const int *의 값을 int * 값에 대입하려고 했기 때문이다.
const_cast를 통해 const int *의 값을 int *의 값으로 변경해서 b에 대입해줬다.
int main(void) {
const int a = 4;
int *b;
// b = &a;
b = const_cast<int *>(&a);
std::cout << &a << ", " << b << std::endl;
}
4. reinterpret_cast (value ot pointer, pointer to value O!)
static_cast는 value to pointer는 가능했지만, pointer to value는 막아놨었다.
둘 다 사용할 수 있게 해주는 타입 변환 연산자가 reunterpret_cast이다.
int serialize(Data* ptr) {
return reinterpret_cast<int>(ptr);
}
Data* deserialize(int raw) {
return reinterpret_cast<Data*>(raw);
}
'개발 언어 > cpp' 카테고리의 다른 글
CPP Lvalue, Rvalue, &&(우측값 레퍼런스) 개념 (0) | 2022.07.27 |
---|---|
CPP explicit 키워드 (0) | 2022.04.27 |
CPP template (0) | 2022.03.18 |
CPP 부모클래스에서 virtual 함수가 하나라도 있으면 소멸자도 무조건 virtual? (0) | 2022.03.10 |
:: Scope Operator(범위 지정 연산자)를 쓰는 3가지 상황 (0) | 2022.02.15 |
댓글