KoreanFoodie's Study

C++ DevNote : 타입 정보 RTTI(Runtime Type Infomation or Runtime Type Identification) 에 대하여 본문

Tutorials/C++ : Expert

C++ DevNote : 타입 정보 RTTI(Runtime Type Infomation or Runtime Type Identification) 에 대하여

GoldGiver 2022. 12. 27. 21:47

C++ 에 대해 공부한 것과, 개발하면서 알게 된 것들을 다룹니다

RTTI

RTTI 는 간단히 말해, 실시간 타입 정보라는 뜻이다. RTTI 라는 단어를 평소에는 잘 들어볼 일이 없다가, dynamic_cast 를 사용하기 위해 비주얼 스튜디오 옵션을 뒤적이던 중 해당 항목을 발견했다.

알다시피, C++ 는 리플렉션 기능을 제공하지 않는다. C# 에서는 리플렉션을 제공하는데, 해당 기능은 런타임에서 클래스 타입, 메소드, 프로퍼티 등의 메타 정보를 제공해 준다.

비주얼 스튜디오에서는 해당 기능을 끄고 킬 수 있게 만들어 놓았다.

Visual Studio 2022 에서 설정

간단히 말해, 해당 기능을 키게 되면 런타임에서 타입에 대한 정보를 얻을 수 있다. RTTI 는 가상 함수가 있는 클래스에 대해서만 동작하며, 이는 클래스의 타입 관련 정보가 vtable 에 같이 저장되기 때문이다. 컴파일러는 컴파일 번역 이후 이름을 실행 파일에 남길 필요가 없어 RTTI 로 정보를 가져온다.

RTTI 가 제대로 동작하기 위해선 모든 클래스에 타입과 관련된 정보를 작성해야 한다. 물론 이것을 키면 프로그램의 성능이 느려지고 용량이 비대해지는 부작용이 있다.

 

C++ 는 RTTI 를 지원하는 세 가지 요소를 갖고 있다.

  • dynamic_cast : 기반 클래스에서 파생 클래스로의 다운 캐스팅 시 사용. 실패할 경우 nullptr 리턴
  • typeid 연산자 : 객체의 타입 식별값을 리턴
  • type_info : 타입에 대한 정보를 저장

 

 

dynamic_cast

dynamic_cast 의 경우 이전 글에 정리한 내용을 참고하자.

추가로 한 마디 덧붙이자면, dynamic_cast 는 참조에서도 사용이 가능하나, 참조에서는 nullptr 에 해당하는 참조값이 없다. 따라서 변환될 수 없을 경우 bad_cast 예외가 발생한다(exception class from typeinfo header file). 

 

 

typeid 연산자, type_info 클래스

typeid 연산자를 사용하기 위해서는 typeinfo 헤더 파일을 include 해주면 된다.

typeid 연산자를 이용해 객체의 데이터형이 동일한지를 파악할 수 있으며, 이는 type_info 객체에 대한 참조를 리턴한다. 이를 위해 type_info 클래스는 테이터형을 비교하는 데 사용할 수 있는 '==' 와  '!=' 연산자를 갖고 있다.

#include <iostream>
#include <typeinfo>

using namespace std;

class A {
public:
    virtual void foo() { cout << "A" << endl; }
};

class B : public A {
public:
    virtual void foo() { cout << "B" << endl; }
};

int main(void) {
    
    A* a = new A();
    B* b = new B();
    
    A* arr[2];
    arr[0] = a;
    arr[1] = b;
    
    for (int i = 0; i < 2; ++i)
    {
        arr[i]->foo();
        if (typeid(B) == typeid(*arr[i]))
        {
            B* bb = (B*)arr[i];
            bb->foo();
            cout << "현재 타입 : " << typeid(*arr[i]).name() << endl;
        }
    }
}

위 코드의 결과값은 다음과 같다 :

A
B
B
현재 타입 : 1B

1B 에서 앞에 붙은 1 은 타입명의 길이를 의미한다. 만일 nullptr 로부터 typeid 를 얻으려 한다면 bad_typeid 예외가 발생한다!

 

RTTI 를 사용하면 프로그램 효율을 떨어뜨릴 수 있으며, 이를 남용하는 것은 좋은 습관이 아니다. 주로 디버깅을 위해 사용하는 것이 권장되고 있다.

물론, typeid 를 활용하면 좋은 경우도 간혹 존재한다. boost::any 에서 any_cast 를 구현하기 위해 typeid 를 사용한 다음 예시 코드를 보자.

template<typename T> any_cast(const any& other) {
   if(typeid(T) != other.type()) throw bad_any_cast();

   //...actual cast here...
}

위에서 T 는 polymorphic 함을 확신할 수 없으므로 dynamic_cast 를 사용할 수 없다. 이 경우에서는 typeid 를 활용해야만 타입 안정성을 보장할 수 있다!

 

참고 : 블로그 1, 블로그 2, StackOverflow

Comments