본문 바로가기

C++

[C++] 캐스트 연산자 (static, dynamic, const, reinterpret)

기본적으로 우리가 사용하는 캐스트 연산자는 강제로 타입을 바꾸기 때문에 위험하다.

예를 들어 문자형 포인터 변수를 정수형 포인터로 캐스팅했을 때 알 수 없는 엉뚱한 번지를 가리킨다.

그래서 좀 더 안전하고 목적에 맞게 쓸 수 있는 캐스트 연산자들이 있다.

 

 

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