В компьютерном программировании, информация о типе среды выполнения или идентификация типа во время выполнения ( RTTI) является особенностью некоторых языков программирования (например, C ++, Object Pascal и Ada ), который предоставляет информацию о объекте типа данных на время выполнения. Информация о типах времени выполнения может быть доступна для всех типов или только для тех типов, которые явно ее имеют (как в случае с Ada). Информация о типах во время выполнения - это специализация более общей концепции, называемой интроспекцией типов.
В исходной конструкции C ++ Бьярн Страуструп не включил информацию о типах во время выполнения, поскольку считал, что этот механизм часто используется не по назначению.
В C ++ RTTI можно использовать для безопасного приведения типов с помощью dynamic_castlt;gt;
оператора и для управления информацией о типе во время выполнения с помощью typeid
оператора и std::type_info
класса. В Object Pascal RTTI можно использовать для выполнения безопасного преобразования типов с помощью as
оператора, тестирования класса, к которому принадлежит объект, с помощью is
оператора и управления информацией о типе во время выполнения с помощью классов, содержащихся в RTTI
модуле (например, классы: TRttiContext, TRttiInstanceType, так далее.). В Ada объекты помеченных типов также хранят тег типа, который позволяет идентифицировать тип этих объектов во время выполнения. in
Оператор может быть использован для испытаний, во время выполнения, если объект относится к типу специфической и может быть безопасно преобразовать в нее.
RTTI доступен только для полиморфных классов, что означает, что у них есть хотя бы один виртуальный метод. На практике это не ограничение, потому что базовые классы должны иметь виртуальный деструктор, чтобы позволить объектам производных классов выполнять надлежащую очистку, если они удалены из базового указателя.
У некоторых компиляторов есть флаги для отключения RTTI. Использование этих флагов может уменьшить общий размер приложения, что делает их особенно полезными при работе с системами с ограниченным объемом памяти.
typeid
Ключевое слово используется для определения класса в качестве объекта на время выполнения. Возвращает ссылку на std::type_info
объект, существующий до конца программы. Использование typeid
в неполиморфном контексте часто предпочтительнее в ситуациях, когда требуется только информация о классе, потому что это всегда процедура с постоянным временем, тогда как может потребоваться пересечь решетку деривации классов своего аргумента во время выполнения. Некоторые аспекты возвращаемого объекта определяются реализацией, например, и нельзя полагаться на согласованность между компиляторами. dynamic_castlt;class_typegt;
typeid
dynamic_cast
std::type_info::name()
Объекты класса std::bad_typeid
выбрасываются, когда выражение для typeid
является результатом применения унарного оператора * к нулевому указателю. Создание исключения для других аргументов нулевой ссылки зависит от реализации. Другими словами, чтобы исключение было гарантировано, выражение должно иметь форму typeid(*p)
где p
- любое выражение, приводящее к нулевому указателю.
#include lt;iostreamgt; #include lt;typeinfogt; class Person { public: virtual ~Person() = default; }; class Employee: public Person {}; int main() { Person person; Employee employee; Person* ptr = amp;employee; Personamp; ref = employee; // The string returned by typeid::name is implementation-defined. std::cout lt;lt; typeid(person).name() lt;lt; std::endl; // Person (statically known at compile-time). std::cout lt;lt; typeid(employee).name() lt;lt; std::endl; // Employee (statically known at compile-time). std::cout lt;lt; typeid(ptr).name() lt;lt; std::endl; // Person* (statically known at compile-time). std::cout lt;lt; typeid(*ptr).name() lt;lt; std::endl; // Employee (looked up dynamically at run-time // because it is the dereference of a // pointer to a polymorphic class). std::cout lt;lt; typeid(ref).name() lt;lt; std::endl; // Employee (references can also be polymorphic) Person* p = nullptr; try { typeid(*p); // Not undefined behavior; throws std::bad_typeid. } catch (...) { } Personamp; p_ref = *p; // Undefined behavior: dereferencing null typeid(p_ref); // does not meet requirements to throw std::bad_typeid // because the expression for typeid is not the result // of applying the unary * operator. }
Вывод (точный вывод зависит от системы и компилятора):
Person Employee Person* Employee Employee
dynamic_cast
Оператор в C ++ используется для понижающего приведения ссылки или указателя на более конкретный тип в иерархии классов. В отличие от static_cast
, целью класса dynamic_cast
должен быть указатель или ссылка на класс. В отличие от приведения типов в стиле C static_cast
и (где проверка типа выполняется во время компиляции), проверка безопасности типа выполняется во время выполнения. Если типы несовместимы, будет выброшено исключение (при работе со ссылками ) или будет возвращен нулевой указатель (при работе с указателями ).
Приведение типов в Java ведет себя аналогичным образом; если приводимый объект на самом деле не является экземпляром целевого типа и не может быть преобразован в один с помощью метода, определенного языком, java.lang.ClassCastException
будет брошен экземпляр.
Предположим, что некоторая функция принимает объект типа в A
качестве аргумента, и желает выполнить некоторую дополнительную работу, если переданный объект является экземпляром B
, в подклассе из A
. Это можно сделать dynamic_cast
следующим образом.
#include lt;arraygt; #include lt;iostreamgt; #include lt;memorygt; #include lt;typeinfogt; using namespace std; class A { public: // Since RTTI is included in the virtual method table there should be at // least one virtual function. virtual ~A() = default; void MethodSpecificToA() { cout lt;lt; "Method specific for A was invoked" lt;lt; endl; } }; class B: public A { public: void MethodSpecificToB() { cout lt;lt; "Method specific for B was invoked" lt;lt; endl; } }; void MyFunction(Aamp; my_a) { try { // Cast will be successful only for B type objects. Bamp; my_b = dynamic_castlt;Bamp;gt;(my_a); my_b.MethodSpecificToB(); } catch (const bad_castamp; e) { cerr lt;lt; " Exception " lt;lt; e.what() lt;lt; " thrown." lt;lt; endl; cerr lt;lt; " Object is not of type B" lt;lt; endl; } } int main() { arraylt;unique_ptrlt;Agt;, 3gt; array_of_a; // Array of pointers to base class A. array_of_a[0] = make_uniquelt;Bgt;(); // Pointer to B object. array_of_a[1] = make_uniquelt;Bgt;(); // Pointer to B object. array_of_a[2] = make_uniquelt;Agt;(); // Pointer to A object. for (int i = 0; i lt; 3; ++i) MyFunction(*array_of_a[i]); }
Вывод в консоль:
Method specific for B was invoked Method specific for B was invoked Exception std::bad_cast thrown. Object is not of type B
Аналогичный вариант MyFunction
можно написать с указателями вместо ссылок :
void MyFunction(A* my_a) { B* my_b = dynamic_castlt;B*gt;(my_a); if (my_b != nullptr) my_b-gt;methodSpecificToB(); else std::cerr lt;lt; " Object is not B type" lt;lt; std::endl; }
В Object Pascal оператор is
используется для проверки типа класса во время выполнения. Он проверяет принадлежность объекта к данному классу, включая классы отдельных предков, присутствующие в дереве иерархии наследования (например, Button1 является TButton класса, который имеет предок: TWinControl → TControl → ТСотропепЬ → TPersistent → TObject, где последний является предком всех классов). Оператор as
используется, когда объект должен обрабатываться во время выполнения, как если бы он принадлежал классу-предку.
Модуль RTTI используется для управления информацией о типе объекта во время выполнения. Этот модуль содержит набор классов, которые позволяют: получать информацию о классе объекта и его предках, свойствах, методах и событиях, изменять значения свойств и вызывать методы. В следующем примере показано использование модуля RTTI для получения информации о классе, к которому принадлежит объект, создания его и вызова его метода. В примере предполагается, что класс TSubject был объявлен в модуле с именем SubjectUnit.
uses RTTI, SubjectUnit; procedure WithoutReflection; var MySubject: TSubject; begin MySubject:= TSubject.Create; try Subject.Hello; finally Subject.Free; end; end; procedure WithReflection; var RttiContext: TRttiContext; RttiType: TRttiInstanceType; Subject: TObject; begin RttiType:= RttiContext.FindType('SubjectUnit.TSubject') as TRttiInstanceType; Subject:= RttiType.GetMethod('Create').Invoke(RttiType.MetaclassType, []).AsObject; try RttiType.GetMethod('Hello').Invoke(Subject, []); finally Subject.Free; end; end;