Информация о типе времени выполнения

редактировать

В компьютерном программировании, информация о типе среды выполнения или идентификация типа во время выполнения ( RTTI) является особенностью некоторых языков программирования (например, C ++, Object Pascal и Ada ), который предоставляет информацию о объекте типа данных на время выполнения. Информация о типах времени выполнения может быть доступна для всех типов или только для тех типов, которые явно ее имеют (как в случае с Ada). Информация о типах во время выполнения - это специализация более общей концепции, называемой интроспекцией типов.

В исходной конструкции C ++ Бьярн Страуструп не включил информацию о типах во время выполнения, поскольку считал, что этот механизм часто используется не по назначению.

СОДЕРЖАНИЕ
  • 1 Обзор
  • 2 C ++ - typeid
    • 2.1 Пример
  • 3 C ++ - dynamic_cast и Java cast
    • 3.1 Пример
  • 4 Delphi / Object Pascal
  • 5 См. Также
  • 6 Ссылки
  • 7 Внешние ссылки
Обзор

В C ++ RTTI можно использовать для безопасного приведения типов с помощью dynamic_castlt;gt;оператора и для управления информацией о типе во время выполнения с помощью typeidоператора и std::type_infoкласса. В Object Pascal RTTI можно использовать для выполнения безопасного преобразования типов с помощью asоператора, тестирования класса, к которому принадлежит объект, с помощью isоператора и управления информацией о типе во время выполнения с помощью классов, содержащихся в RTTIмодуле (например, классы: TRttiContext, TRttiInstanceType, так далее.). В Ada объекты помеченных типов также хранят тег типа, который позволяет идентифицировать тип этих объектов во время выполнения. inОператор может быть использован для испытаний, во время выполнения, если объект относится к типу специфической и может быть безопасно преобразовать в нее.

RTTI доступен только для полиморфных классов, что означает, что у них есть хотя бы один виртуальный метод. На практике это не ограничение, потому что базовые классы должны иметь виртуальный деструктор, чтобы позволить объектам производных классов выполнять надлежащую очистку, если они удалены из базового указателя.

У некоторых компиляторов есть флаги для отключения RTTI. Использование этих флагов может уменьшить общий размер приложения, что делает их особенно полезными при работе с системами с ограниченным объемом памяти.

C ++ - typeid

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
C ++ - dynamic_cast и Java cast

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; }
Delphi / Object Pascal

В 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;
Смотрите также
Рекомендации
Внешние ссылки
Последняя правка сделана 2023-03-31 10:39:52
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте