기본적으로 우리가 사용하는 캐스트 연산자는 강제로 타입을 바꾸기 때문에 위험하다.
예를 들어 문자형 포인터 변수를 정수형 포인터로 캐스팅했을 때 알 수 없는 엉뚱한 번지를 가리킨다.
그래서 좀 더 안전하고 목적에 맞게 쓸 수 있는 캐스트 연산자들이 있다.
static_cast<타입>(대상)
static_cast는 논리적으로 변환 가능한 타입만 변환한다. 예를 들어 실수형를 정수형으로 변환하거나 double과 float의 변환 등은 가능하지만 포인터 타입을 변환하는 것은 상속 관계일 떄만 허용한다.
void main()
{
Parent P,*pP;
Child C,*pC;
int i=1;
pP=static_cast<Parent *>(&C); // 가능
pC=static_cast<Child *>(&P); // 가능하지만 위험
pP=static_cast<Parent *>(&i); // 에러
pC=static_cast<Child *>(&i); // 에러
}
자식은 부모의 모든 멤버를 가지고 있기 때문에 자식의 번지를 부모 포인터 타입으로 캐스팅하는 것은 가능하다. 이것을 업 캐스팅이라고 하고 그 반대를 다운 캐스팅이라고 한다. 다운 캐스팅은 부모가 자식의 모든 멤버를 갖고 있지 않기 때문에 위험하다.
dynamic_cast<타입>(대상)
dynamic_cast는 포인터는 포인터끼리 레퍼런스는 레퍼런스끼리 변환해야 하고 포인터끼리도 상속 계층에 속한 클래스끼리만 변환할 수 있다. 업 캐스팅은 이 연산자 필요 없이 당연히 가능하고 다운 캐스팅 할 때는 부모 포인터가 실제 자식 객체를 가리키고 있을 때만 다운 캐스팅이 가능하다. 부모 포인터가 부모 객체를 가리키고 있을 경우에는 다운 캐스팅을 허가하지 않고 NULL을 리턴한다.
void main()
{
Parent P,*pP,*pP2;
Child C,*pC,*pC2;
pP=&P;
pC=&C;
pP2=dynamic_cast<Parent *>(pC); // 업 캐스팅-항상 안전하다.
pC2=dynamic_cast<Child *>(pP2); // 다운 캐스팅-경우에 따라 다르다.
printf("pC2 = %p\n",pC2);
pC2=dynamic_cast<Child *>(pP); // 캐스팅 불가능
printf("pC2 = %p\n",pC2);
}
pP2는 첫줄에 업 캐스팅에 의해 부모포인터 타입으로 캐스팅 된 자식포인터 pC를 가리킨다. 두번째 줄에서 pP2는 자식을 가리키므로 pP2의 다운 캐스팅은 성공한다. 하지만 pP를 다운 캐스팅 할 때는 pP가 부모 객체를 가리키기 때문에 다운 캐스팅을 허가할 수 없다. NULL을 리턴한다. static_cast와 차이점이 static_cast는 다운 캐스팅 할 때 무조건 허가하는 것에 비해 안전한 캐스팅만 허가한다는 점이다. dynamic_cast는 실행중에 타입을 판별할 수 있어야하므로 RTTI 옵션이 켜져있어야 한다. RTTI 옵션은 부모 함수가 virtual 함수를 하나라도 가져야 하는 것을 의미한다. 잘못된 대상이라면 NULL 리턴.
const_cast<타입>(대상)
const_cast 연산자는 상수 지시 포인터를 비상수 지시 포인터로 바꾸고 싶을 때 사용한다.
void main()
{
char str[]="string";
const char *c1=str;
char *c2;
c2=const_cast<char *>(c1);
c2[0]='a';
printf("%s\n",c2);
}
c1이 가리키는 대상이 변경 가능한 대상이라는 것이 확실하면 c1을 char*로 캐스팅하는 것이 가능하다. 위 코드의 경우 변경할 수 있는 문자형 배열이기 때문에 캐스팅이 가능하지만 str이 char*로 선언되어 있는 문자형 포인터라면 변경하는 것이 위험하다. 가리키는 번지가 달라진다면 어떤 것을 가리킬지 모르기 때문이다.(문자형 배열은 데이터를 복사한 사본이고 문자형 포인터는 직접 가리키는 것이기 때문) 이 연산자는 변수의 상수성만 변경할 수 있고 타입 변환은 허용하지 않는다. 그래서 const_cast <int*>나 const_cast<double>,은 에러로 처리한다.
이 연산자는 상수성의 변경만이 목적으로 코드를 읽을 때 어떤 의도인지 쉽게 파악할 수 있다는 장점이 있고 그냥 캐스트 연산자를 사용했을 때 상수성 변경이 아닌 그 타입으로 변환하는 것을 방지하기 위해 사용한다.
reinterpret_cast<타입>(대상)
reinterpret_cast는 임의의 포인터 타입끼리 변환을 허용하는 위험한 연산자이다. 정수형과 포인터간의 변환도 허용하므로 아래 코드가 문제 없이 동작한다.
int *pi;
char *pc;
pi=reinterpret_cast<int *>(12345678);
pc=reinterpret_cast<char *>(pi);
이 캐스트 연산자들은 기존 C 캐스팅 스타일 (타입)이 포괄적인 경우를 포함하기 때문에 캐스팅이 어떤 의도인지 쉽게 알기 위해서 이 연산자들을 사용한다.
'C++' 카테고리의 다른 글
| [C++] 네임 스페이스 (0) | 2022.01.08 |
|---|---|
| [C++] 멤버 포인터 변수, 연산자 (0) | 2022.01.07 |
| [C++] RTTI (0) | 2022.01.07 |
| [C++] 예외 객체 (0) | 2022.01.07 |
| [C++] 예외 처리 (0) | 2022.01.07 |