런타임 타입 정보(RTTI, Run-Time Type Information 또는 Run-Time Type Identification[1])는런타임 시 객체의자료형에 관한 정보를 드러내는 C++ 메커니즘을 가리킨다. RTTI는 간단한 정수나 문자와 같은 자료형 또는 제네릭 타입에 적용할 수 있다. 이것은타입 인트로스펙션이라고 부르는 더 일반적인 개념의 C++ 특수화이다. 델파이 (오브젝트 파스칼)같은 다른 프로그래밍 언어들에서도 비슷한 메카니즘이 존재한다.
원래 C++ 디자인에서,비야네 스트롭스트룹은 이 메커니즘이 자주 오용된다고 생각했기 때문에, RTTI를 포함시키지 않았다.[2]
dynamic_cast<> 연산과 typeid 연산자가 C++에서 RTTI를 구성한다. C++ 런타임 타입 정보는 런타임시에 안전한형 변환과 타입 조작의 수행을 허용한다. RTTI는 오직다형적인 클래스들에서 사용가능한데, 이것은 적어도 하나의가상 함수를 갖는다는 것을 의미한다. 실제로 이 점은 만약 이것들이 베이스 포인터에서 지워진 경우, 베이스 클래스들은 (반드시 상속받은 클래스들의 객체들이 적절한 클린업을 수행하게 하는)가상 소멸자를 가져야 하기 때문에 한계라고 할 수 없다. RTTI는 어떤 컴파일러들에서는 선택사항이다; 프로그래머는 컴파일 시에 함수를 포함할 지를 선택할 수 있다. 프로그램은 이것을 사용하지 않더라도 RTTI를 사용가능하게 만들 리소스 비용이 존재할 것이다.
typeid 키워드는 런타임 시에객체의클래스를 선택하는데 사용된다. 이것은 std::type_info 객체에 대한 참조를 반환하는데, 프로그램의 종료 시까지 존재하게 된다.[3] 다형성이 사용되지 않은 문맥에서 단지 클래스 정보가 필요한 경우 typeid의 사용은 종종 dynamic_cast<class_type> 보다 선호된다. 왜냐하면 dynamic_cast가 반드시 런타임시에 이것의 인자의 클래스 상속 래티스를 순회해야하는 반면, typeid는 시상수 프로시저이기 때문이다. 반환된 객체의 어떤 면들은 std::type_info::name() 처럼 구현 시에 정의되며, 모든 컴파일러들에서 일정하다고 할 수는 없다.
클래스 std::bad_typeid의 객체들은널 포인터에서 typeid를 위한 표현이unary * 연산자를 적용하는 것의 결과일 때 던져진다. 예외가 다른 널 참조 인자에 던져지든지 말든지는 구현에 종속적이다. 즉, p가 널 포인터를 야기하는 어느 표현일 때, 보장되어야 할 예외를 위해 표현은 반드시typeid(*p) 형태를 취해야 한다.
#include<iostream> // cout#include<typeinfo> // for 'typeid'classPerson{public:virtual~Person(){}};classEmployee:publicPerson{};intmain(){Personperson;Employeeemployee;Person*ptr=&employee;Person&ref=employee;// The string returned by typeid::name is implementation-definedstd::cout<<typeid(person).name()<<std::endl;// Person (statically known at compile-time)std::cout<<typeid(employee).name()<<std::endl;// Employee (statically known at compile-time)std::cout<<typeid(ptr).name()<<std::endl;// Person* (statically known at compile-time)std::cout<<typeid(*ptr).name()<<std::endl;// Employee (looked up dynamically at run-time// because it is the dereference of a// pointer to a polymorphic class)std::cout<<typeid(ref).name()<<std::endl;// Employee (references can also be polymorphic)Person*p=nullptr;try{typeid(*p);// not undefined behavior; throws std::bad_typeid}catch(...){}Person&pRef=*p;// Undefined behavior: dereferencing nulltypeid(pRef);// does not meet requirements to throw std::bad_typeid// because the expression for typeid is not the result// of applying the unary * operator}
결과 (시스템에 따라 정확한 결과는 다르다.):
PersonEmployeePerson*EmployeeEmployee
C++의 dynamic_cast 연산자는 클래스 계층에서 참조나 포인터를 더 구체적인 타입으로 다운캐스팅하는데 사용된다. static_cast와 달리, dynamic_cast 의 대상은 반드시 클래스에 대한 포인터나 참조여야 한다.static_cast 와 C-스타일 타입캐스트(타입 검사가 컴파일 시에 이루어지는)와 달리, 타입 안전 검사는런타임 시에 수행된다. 만약 타입들이 호환되지 않는다면, 예외가 던져지거나(참조를 다룰 때)널 포인터가 반환될(포인터를 다룰 때) 것이다.
자바 타입캐스트도 비슷하게 동작한다; 만약 던져진 객체가 실제로 대상 타입의 인스턴스가 아니고, 인스턴스인 언어로 정의된 방식으로 변환되지 못한다면, java.lang.ClassCastException 의 인스턴스가 던져질 것이다.[4]
몇몇 함수가 타입 A의 객체를 인자로 받으며, 만약 전달받은 객체가B의 인스턴스이고, A의 서브클래스라면, 몇몇 추가적인 연산을 수행하길 바란다고 가정하자. 이것은 다음과 같이dynamic_cast 를 사용함으로써 달성될 수 있다.
#include<typeinfo> // For std::bad_cast#include<iostream> // For std::cout, std::err, std::endl etc.classA{public:// Since RTTI is included in the virtual method table there should be at least one virtual function.virtual~A(){};voidmethodSpecificToA(){std::cout<<"Method specific for A was invoked"<<std::endl;};};classB:publicA{public:voidmethodSpecificToB(){std::cout<<"Method specific for B was invoked"<<std::endl;};virtual~B(){};};voidmy_function(A&my_a){try{B&my_b=dynamic_cast<B&>(my_a);// cast will be successful only for B type objects.my_b.methodSpecificToB();}catch(conststd::bad_cast&e){std::cerr<<" Exception "<<e.what()<<" thrown."<<std::endl;std::cerr<<" Object is not of type B"<<std::endl;}}intmain(){A*arrayOfA[3];// Array of pointers to base class (A)arrayOfA[0]=newB();// Pointer to B objectarrayOfA[1]=newB();// Pointer to B objectarrayOfA[2]=newA();// Pointer to A objectfor(inti=0;i<3;i++){my_function(*arrayOfA[i]);deletearrayOfA[i];// delete object to prevent memory leak}}
콘솔 결과:
Method specific for B was invokedMethod specific for B was invokedException std::bad_cast thrown.Object is not of type B
my_function의 비슷한 버전이 참조 대신 포인터로 쓰여질 수 있다:
voidmy_function(A*my_a){B*my_b=dynamic_cast<B*>(my_a);if(my_b!=nullptr)my_b->methodSpecificToB();elsestd::cerr<<" Object is not B type"<<std::endl;}