본문 바로가기

C++

[C++] RTTI

void func(Parent *p)
{
     p->PrintMe();
     ((Child *)p)->PrintNum();
} 

void main()
{
     Parent p;
     Child c(5);
     
     func(&c);
     func(&p);
}

 

위 코드에서 Parent 클래스와 Parent의 파생클래스 Child 클래스에만 함수 PrintNum()이 존재한다고 하자. 그렇다면 func의 인수로 Child클래스가 들어가면 상관없지만 Parent클래스가 들어가면 Child 클래스로 강제로 캐스팅되고 Parent클래스에는 PrintNum이 없기 때문에 오류가 발생한다. 그래서 func의 인수의 타입을 실시간으로 알수 있게 하는 C++의 기능이 RTTI이다. RTTI는 클래스의 타입정보가 vtable에 저장되어있기 때문에 가상함수가 있는 클래스에서만 작동(?)한다.

RTTI를 사용하려면 <typeinfo>헤더파일을 포함하고 프로젝트 설정에서 RTTI 옵션을 선택해야 한다.

 

void main()
{
     Parent P,*pP;
     Child C,*pC;
     pP=&P;
     pC=&C;
     
     printf("P=%s, pP=%s, *pP=%s\n",
          typeid(P).name(),typeid(pP).name(),typeid(*pP).name());
     printf("C=%s, pC=%s, *pC=%s\n",
          typeid(C).name(),typeid(pC).name(),typeid(*pC).name());

     pP=&C;

     printf("pP=%s, *pP=%s\n",
          typeid(pP).name(),typeid(*pP).name());
}

출력값:

P=class Parent, pP=class Parent *, *pP=class Parent

C=class Child, pC=class Child *, *pC=class Child

pP=class Parent *, *pP=class Child

 

typeid 연산자로 인수의 타입을 알 수 있다. pP가 C의 주소를 가리키고 난 다음에는 pP 자체는 Parent포인터라서 Parent 타입이지만 (*pP)는  Child이기 때문에 Child 타입이다.

 

void func(Parent *p)
{
     p->PrintMe();
     if (typeid(*p)==typeid(Child)) {
          ((Child *)p)->PrintNum();
     } else {
          puts("이 객체는 num을 가지고 있지 않습니다.");
     }
}

 

func함수를 typeid연산자를 이용해 바꾼 코드이다. *p와 Child가 같은지 비교해 Child일때만 PrintNum을 호출한다.

 

RTTI를 사용하지 않고 기반 클래스에 protected로 Name이라는 멤버를 만들어서 부모인지 자식인지 구분할 수 있게 할 수도 있다. 하지만 클래스마다 하나씩 생성되는 type_info와 달리 객체 마다 Name이 하나씩 늘어나는 단점이 있다.

'C++' 카테고리의 다른 글

[C++] 멤버 포인터 변수, 연산자  (0) 2022.01.07
[C++] 캐스트 연산자 (static, dynamic, const, reinterpret)  (0) 2022.01.07
[C++] 예외 객체  (0) 2022.01.07
[C++] 예외 처리  (0) 2022.01.07
[C++] 클래스 템플릿  (0) 2022.01.06