В компьютерном программировании, информации о типе среды выполнения или идентификации типа во время выполнения ( RTTI ) [1] является особенностью C ++ и Object Pascal языков программирования , который предоставляет информацию о объекте типа данных на время выполнения . Информация о типах времени выполнения может применяться к простым типам данных, таким как целые числа и символы, или к универсальным типам. Это специализация более общей концепции, называемой интроспекцией типов .
В первоначальном проекте C ++ Бьярн Страуструп не включил информацию о типах во время выполнения, поскольку считал, что этот механизм часто используется неправильно. [2]
Обзор
В C ++ RTTI можно использовать для безопасного приведения типов с помощью dynamic_cast<>
оператора и для управления информацией о типе во время выполнения с помощью typeid
оператора и std::type_info
класса. В Object Pascal RTTI можно использовать для выполнения безопасного преобразования типов с помощью as
оператора, тестирования класса, к которому принадлежит объект, с помощью is
оператора и управления информацией о типе во время выполнения с помощью классов, содержащихся в RTTI
модуле [3] (т.е. классы: TRttiContext , TRttiInstanceType и т. Д.).
RTTI доступен только для полиморфных классов , что означает, что у них есть хотя бы один виртуальный метод . На практике это не ограничение, потому что базовые классы должны иметь виртуальный деструктор, чтобы позволить объектам производных классов выполнять надлежащую очистку, если они удалены из базового указателя.
RTTI является необязательным для некоторых компиляторов; программист может выбрать во время компиляции, включать ли эту функциональность. Обеспечение доступности RTTI может потребовать значительных ресурсов, даже если программа его не использует.
C ++ - typeid
typeid
Ключевое слово используется для определения класса в качестве объекта на время выполнения . Возвращает ссылку на std::type_info
объект, существующий до конца программы. [4] Использование typeid
в неполиморфном контексте часто предпочтительнее в ситуациях, когда требуется только информация о классе, потому что это всегда процедура с постоянным временем , тогда как может потребоваться пересечь решетку деривации классов своего аргумента в время выполнения. [ необходима цитата ] Некоторые аспекты возвращаемого объекта определяются реализацией, например , и не могут использоваться компиляторами для обеспечения согласованности.dynamic_cast<class_type>
typeid
dynamic_cast
std::type_info::name()
Объекты класса std::bad_typeid
выбрасываются, когда выражение для typeid
является результатом применения унарного оператора * к нулевому указателю . Выбрасывается ли исключение для других аргументов нулевой ссылки, зависит от реализации. Другими словами, чтобы исключение было гарантировано, выражение должно иметь форму typeid(*p)
где p
- любое выражение, приводящее к нулевому указателю.
Пример
#include #include class Person { public : virtual ~ Person () = default ; };class Employee : public Person {};int main () { Человек person ; Сотрудник сотрудник ; Человек * ptr = & employee ; Человек & ref = сотрудник ; // Строка, возвращаемая typeid :: name, определяется реализацией. std :: cout << typeid ( человек ). имя () << std :: endl ; // Человек (статически известен во время компиляции). std :: cout << typeid ( сотрудник ). имя () << std :: endl ; // Сотрудник (статически известен во время компиляции). std :: cout << typeid ( ptr ). имя () << std :: endl ; // Человек * (статически известен во время компиляции). std :: cout << typeid ( * ptr ). имя () << std :: endl ; // Сотрудник (просматривается динамически во время выполнения // потому что это разыменование указателя // на полиморфный класс). std :: cout << typeid ( ref ). имя () << std :: endl ; // Сотрудник (ссылки также могут быть полиморфными) Человек * p = nullptr ; попробуйте { typeid ( * p ); // Не неопределенное поведение; выбрасывает std :: bad_typeid. } поймать (...) { } Человек & p_ref = * p ; // Неопределенное поведение: разыменование null typeid ( p_ref ); // не соответствует требованиям для выдачи std :: bad_typeid // потому что выражение для typeid не является результатом // применения унарного оператора *. }
Вывод (точный вывод зависит от системы и компилятора):
ЧеловекРаботникЧеловек*РаботникРаботник
C ++ - dynamic_cast и Java cast
dynamic_cast
Оператор в C ++ используется для понижающего приведения ссылки или указателя на более конкретный тип в иерархии классов . В отличие от static_cast
, целью класса dynamic_cast
должен быть указатель или ссылка на класс . В отличие от приведения типов в стиле Cstatic_cast
и (где проверка типа выполняется во время компиляции), проверка безопасности типа выполняется во время выполнения . Если типы несовместимы, будет выброшено исключение (при работе со ссылками ) или будет возвращен нулевой указатель (при работе с указателями ).
Приведение типов в Java ведет себя аналогичным образом; если приводимый объект на самом деле не является экземпляром целевого типа и не может быть преобразован в один с помощью метода, определенного языком, java.lang.ClassCastException
будет брошен экземпляр . [5]
Пример
Предположим , что некоторая функция принимает объект типа в A
качестве аргумента, и желает выполнить некоторую дополнительную работу , если переданный объект является экземпляром B
, в подклассе из A
. Это можно сделать dynamic_cast
следующим образом.
#include <массив>#include #include <память>#include используя пространство имен std ;class A { public : // Поскольку RTTI включен в таблицу виртуальных методов, должна быть // как минимум одна виртуальная функция. виртуальный ~ A () = по умолчанию ; void MethodSpecificToA () { cout << "Метод, специфичный для A, был вызван" << endl ; } };class B : public A { public : void MethodSpecificToB () { cout << "Метод, специфичный для B, был вызван" << endl ; } };void MyFunction ( A & my_a ) { try { // Приведение будет успешным только для объектов типа B. B & my_b = динамическое_кастирование < B &> ( my_a ); my_b . MethodSpecificToB (); } catch ( const bad_cast & e ) { cerr << "Исключение" << e . what () << "брошено." << endl ; cerr << "Объект не относится к типу B" << endl ; } }int main () { массив < unique_ptr < A > , 3 > array_of_a ; // Массив указателей на базовый класс A. array_of_a [ 0 ] = make_unique < B > (); // Указатель на объект B. array_of_a [ 1 ] = make_unique < B > (); // Указатель на объект B. array_of_a [ 2 ] = make_unique < > (); // Указатель на объект. for ( int i = 0 ; i < 3 ; ++ i ) MyFunction ( * array_of_a [ i ]); }
Вывод в консоль:
Был вызван метод, специфичный для BБыл вызван метод, специфичный для BВыброшено исключение std :: bad_cast.Объект не типа B
Аналогичный вариант MyFunction
можно написать с указателями вместо ссылок :
void MyFunction ( A * my_a ) { B * my_b = dynamic_cast < B *> ( my_a ); если ( my_b ! = nullptr ) my_b -> methodSpecificToB (); else std :: cerr << "Объект не относится к типу B" << std :: endl ; }
Delphi / Object Pascal
В Object Pascal оператор is
используется для проверки типа класса во время выполнения . Он проверяет принадлежность объекта к данному классу, включая классы отдельных предков , присутствующие в дереве иерархии наследования (например , Button1 является TButton класса , который имеет предок: TWinControl → TControl → ТСотропепЬ → TPersistent → TObject , где последний является предком всех классов). Оператор as
используется, когда объект должен обрабатываться во время выполнения, как если бы он принадлежал классу-предку.
Модуль RTTI используется для управления информацией о типе объекта во время выполнения. Этот модуль содержит набор классов, которые позволяют: получать информацию о классе объекта и его предках, свойствах, методах и событиях, изменять значения свойств и вызывать методы. В следующем примере показано использование модуля RTTI для получения информации о классе, к которому принадлежит объект, создания его и вызова его метода. В примере предполагается, что класс TSubject был объявлен в модуле с именем SubjectUnit.
использует RTTI , SubjectUnit ;процедура WithoutReflection ; var MySubject : TSubject ; начать MySubject : = TSubject . Создать ; попробуйте тему . Привет ; наконец Тема . Бесплатно ; конец ; конец ;процедура WithReflection ; var RttiContext : TRttiContext ; RttiType : TRttiInstanceType ; Тема : TObject ; begin RttiType : = RttiContext . FindType ( 'SubjectUnit.TSubject' ) как TRttiInstanceType ; Тема : = RttiType . GetMethod ( 'Создать' ) . Invoke ( RttiType . MetaclassType , []) . AsObject ; попробуйте RttiType . GetMethod ( 'Привет' ) . Invoke ( Тема , []) ; наконец Тема . Бесплатно ; конец ; конец ;
Смотрите также
- Вывод типа
- Типа самоанализ
- тип
- Отражение (информатика)
- Шаблон (C ++)
Рекомендации
- ^ Sun Microsystems (2000). «Идентификация типа среды выполнения» . Руководство по программированию на C ++ . Oracle . Проверено 16 апреля 2015 года .
- ^ Бьярне Страуструп (март 1993 г.). «История C ++: 1979—1991» (PDF) . Бьярне Страуструп. п. 50 . Проверено 18 мая 2009 .
- ^ «Работа с RTTI - RAD Studio» . docwiki.embarcadero.com . Проверено 6 июня 2021 .
- ^ Стандарт C ++ (ISO / IEC14882) раздел 5.2.8 [expr.typeid], 18.5.1 [lib.type.info] - http://cs.nyu.edu/courses/fall11/CSCI-GA.2110- 003 / документы / c ++ 2003std.pdf
- ^ http://docs.oracle.com/javase/8/docs/api/java/lang/ClassCastException.html
Внешние ссылки
- dynamic_cast оператор в IBM Mac OS X Compilers
- dynamic_cast оператор в MSDN