RTTI , 캐스팅

2015. 3. 31. 04:43프로그래밍/C/C++

728x90
728x90

1. RTTI?

RTTI (runtime type information)

RTTI는 C++ 컴파일러 내에 포함되어 있는 기능으로서, 객체의 유형을 실행 시에 결정할 수 있도록 허용한다. C++이나 다른 고급언어의 컴파일러에서 가지고 있는 이 기능은, 메모리 상주 객체에 유형 정보를 추가한다. 이렇게 함으로써, 실행 시스템은 객체의 캐스트가 유효한 것인지를 확실히 하기 위해, 그 객체가 특정 유형인지를 결정할 수 있다. 객체 기술의 기본 원리 중 하나가, 실행시 객체를 동적으로 변화시킬 수 있는 능력인 polymorphism이다. - 텀즈 http://www.terms.co.kr/RTTI.htm


2. 구성 요소

type_info 구조체 : 실행 시간에 확인하고자 하는 타입에 대한 정보 저장 구조체.

typeid 연산자 : 객체 타입을 식별하고 반환하는 연산자.

dynamic_cast 연산자 : 실행 시간에 실제 객체 타입으로 형변환하는 연산자.



3. dynamic_cast의 문제

안전하게 down cast(부모형에서 자식형으로 형변환)을 할 수 있다. 하지만 일단 수행 속도가 느리다.

내부적으로 RTTI 정보를 체크하고 변환할 객체에 대한 형식 정보를 비교하는데 이것에서 오버헤드가 발생한다.


4. upcasting, downcasting

upcasting - 상속 계층의 위쪽으로 이동하는 변환, 캐스트 연산자를 사용하지 않아도 항상 가능한 대입, 언제나 안전!

downcasting - 상속 계층의 아래쪽으로 이동하는 변환, 캐스트 연산자의 도움 없이는 허가되지 않음, 부모 객체가 자식 클래스의 모든 멤버를 가지고 있지 않을 수 있으므로 위험함. 


5. C++ 캐스트 연산자(+예제)

- static_cast<type>(expression)

캐스팅이 묵시적으로 일어나는 경우 이를 명시적으로 형변환하겠다라고 분명히 해주는 용도로 쓰인다. 

묵시적 캐스트(implicit cast)와 일차적으로 동일하나, 논리적으로 변환 가능한 타입만 변환해준다. 

클래스의 경우엔 상속 관계를 따져보고 변환하며 상호호환 관계가 아니라면 컴파일 에러를 발생시켜 개발자가 에러를 쉽게 찾을 수 있게 해준다.


1
2
3
4
5
6
7
8
9
10
11
void main()
{
     char *str="korea";
     int *pi;
     double d=123.456;
     int i;
  
     i=static_cast<int>(d);                  // 가능
     pi=static_cast<int *>(str);            // 에러
     pi=(int *)str;                          // 가능
}
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Parent { };
class Child : public Parent { };
  
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);       // 에러
}
cs



- dynamic_cast<type>(expression)

dynamic_cast 연산자는 포인터끼리 또는 레퍼런스끼리 변환하는데 사용한다. 

포인터를 레퍼런스로 바꾸거나 레퍼런스를 포인터로 변환하는 것은 상식적으로 필요하지도 않고 가능하지도 않다. 

포인터끼리 변환할 때도 반드시 상속 계층에 속한 클래스끼리만 변환할 수 있다.

(부모 or 형제(sibling)) nt *를 char *로 변환하거나 Parent *를 int *로 변환하는 것은 안된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Parent
{
public:
     virtual void PrintMe() { printf("I am Parent\n"); }
};
  
class Child : public Parent
{
private:
     int num;
  
public:
     Child(int anum=1234) : num(anum) { }
     virtual void PrintMe() { printf("I am Child\n"); }
     void PrintNum() { printf("Hello Child=%d\n",num); }
};
  
void main()
{
     Parent P,*pP,*pP2;
     Child C,*pC,*pC2;
     pP=&P;
     pC=&C;
  
     pP2=dynamic_cast<Parent *>(pC);       // 업 캐스팅-항상 안전하다.
     pC2=dynamic_cast<Child *>(pP2);        // pP2는 사실 Child를 가리키므로 가능. Child가 아니라면 불가능.
     printf("pC2 = %p\n",pC2);
     pC2=dynamic_cast<Child *>(pP);         // 캐스팅 불가능. NULL 리턴.
     printf("pC2 = %p\n",pC2);
}
cs


- const_cast<type>(expression)

const_cast의 용도는 간단하다. 오직 const 또는 volatile의 속성을 변경할 때 사용한다. 

상수 지시 포인터를 비상수 지시 포인터로 잠시 바꾸고 싶을 때 const_cast 연산자를 쓴다. 

반대의 경우도 물론 이 연산자를 사용할 수 있겠지만 비상수 지시 포인터는 

상수 지시 포인터로 항상 변환 가능하므로 캐스트 연산자를 쓸 필요가 없다. 그냥 대입만 하면 된다.


1
2
3
4
5
6
7
8
9
10
void main()
{
     char str[]="string";
     const char *c1=str;
     char *c2;
  
     c2=const_cast<char *>(c1);
     c2[0]='a';
     printf("%s\n",c2);
}
cs



reinterpret_cast<type>(expression)

이 캐스트 연산자는 임의의 포인터 타입끼리 변환을 허용하는 상당히 위험한 캐스트 연산자이다. 

심지어 정수형과 포인터간의 변환도 허용한다. 

정수형값을 포인터 타입으로 바꾸어 절대 번지를 가리키도록 한다거나 할 때 이 연산자를 사용한다.


1
2
3
int *pi;char *pc; 
pi=reinterpret_cast<int *>(12345678);
pc=reinterpret_cast<char *>(pi);
cs


728x90
반응형

'프로그래밍 > C/C++' 카테고리의 다른 글

2차원 벡터 사용하기  (1) 2015.07.08
비트 연산으로 정수에서 이진수 0,1 뽑아내기  (0) 2015.07.08
[C++] 가상 함수 테이블  (0) 2015.04.01
오버라이딩(Overriding)  (0) 2015.03.31
가변인자  (0) 2015.03.31
friend  (0) 2015.03.30
__forceinline, __inline, inline 함수 조사  (0) 2015.03.29