Информация о типе времени выполнения
В компьютерном программировании информация о типе времени выполнения или идентификация типа времени выполнения ( RTTI ). [1] является особенностью некоторых языков программирования (таких как C++ , [2] Объектный Паскаль и Ада [3] объекта ), который предоставляет информацию о типе данных во время выполнения . Информация о типах времени выполнения может быть доступна для всех типов или только для тех типов, которые ее явно содержат (как в случае с Ada). Информация о типах во время выполнения — это специализация более общей концепции, называемой интроспекцией типов .
В первоначальный проект C++ Бьёрн Страуструп не включил информацию о типах во время выполнения, поскольку считал, что этим механизмом часто злоупотребляют. [4]
Обзор
[ редактировать ]В C++ RTTI можно использовать для безопасного приведения типов с помощью dynamic_cast<>
оператор и манипулировать информацией о типе во время выполнения с помощью оператора typeid
оператор и std::type_info
сорт. В Object Pascal RTTI можно использовать для безопасного приведения типов с помощью as
оператор, проверьте класс, к которому принадлежит объект, с помощью is
оператор и манипулировать информацией о типе во время выполнения с помощью классов, содержащихся в RTTI
единица [5] (т.е. классы: TRttiContext , TRttiInstanceType и т. д.). В Ada объекты тегированных типов также хранят тег типа, который позволяет идентифицировать тип этого объекта во время выполнения. in
Оператор можно использовать для проверки во время выполнения, принадлежит ли объект определенному типу и может ли он быть безопасно преобразован в него. [6]
RTTI доступен только для полиморфных классов , что означает, что они имеют хотя бы один виртуальный метод . На практике это не является ограничением, поскольку базовые классы должны иметь виртуальный деструктор , позволяющий объектам производных классов выполнять правильную очистку в случае их удаления из базового указателя.
Некоторые компиляторы имеют флаги для отключения RTTI. Использование этих флагов может уменьшить общий размер приложения, что делает их особенно полезными при работе с системами с ограниченным объемом памяти. [7]
C++ — типизированный идентификатор
[ редактировать ]The typeid
зарезервированное слово (ключевое слово) используется для определения класса объекта во время выполнения . Он возвращает ссылку на std::type_info
объект, который существует до конца программы. [8] Использование typeid
, в неполиморфном контексте, часто предпочтительнее dynamic_cast<class_type>
в ситуациях, когда необходима только информация о классе, потому что typeid
всегда является процедурой постоянного времени , тогда как dynamic_cast
возможно, потребуется пройти решетку вывода классов своего аргумента во время выполнения. [ нужна ссылка ] Некоторые аспекты возвращаемого объекта определяются реализацией, например: std::type_info::name()
, и нельзя полагаться на то, что все компиляторы будут согласованными.
Объекты класса std::bad_typeid
выбрасываются, когда выражение для typeid
является результатом применения унарного оператора * к нулевому указателю . Будет ли выброшено исключение для других аргументов нулевой ссылки, зависит от реализации. Другими словами, чтобы исключение было гарантировано, выражение должно иметь вид typeid(*p)
где p
любое выражение, приводящее к нулевому указателю.
Пример
[ редактировать ]#include <iostream>
#include <typeinfo>
class Person {
public:
virtual ~Person() = default;
};
class Employee : public Person {};
int main() {
Person person;
Employee employee;
Person* ptr = &employee;
Person& ref = employee;
// The string returned by typeid::name is implementation-defined.
std::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& 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++ – динамическое_каст и приведение Java
[ редактировать ]The dynamic_cast
Оператор в C++ используется для понижения ссылки или указателя на более конкретный тип в иерархии классов . В отличие от static_cast
, цель dynamic_cast
должен быть указателем или ссылкой на класс . В отличие от static_cast
и приведение типов в стиле C (где проверка типа происходит во время компиляции), проверка безопасности типа выполняется во время выполнения. Если типы несовместимы, исключение будет выдано (при работе со ссылками ) или нулевой указатель будет возвращен (при работе с указателями ).
Приведение типов Java ведет себя аналогично; если приводимый объект на самом деле не является экземпляром целевого типа и не может быть преобразован в него с помощью метода, определенного языком, экземпляр java.lang.ClassCastException
будет брошен. [9]
Пример
[ редактировать ]Предположим, некоторая функция принимает объект типа A
в качестве аргумента и желает выполнить некоторую дополнительную операцию, если переданный объект является экземпляром B
, подкласс A
. Это можно сделать с помощью dynamic_cast
следующее.
#include <array>
#include <iostream>
#include <memory>
#include <typeinfo>
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 << "Method specific for A was invoked" << endl;
}
};
class B: public A {
public:
void MethodSpecificToB() {
cout << "Method specific for B was invoked" << endl;
}
};
void MyFunction(A& my_a) {
try {
// Cast will be successful only for B type objects.
B& my_b = dynamic_cast<B&>(my_a);
my_b.MethodSpecificToB();
} catch (const bad_cast& e) {
cerr << " Exception " << e.what() << " thrown." << endl;
cerr << " Object is not of type B" << endl;
}
}
int main() {
array<unique_ptr<A>, 3> array_of_a; // Array of pointers to base class A.
array_of_a[0] = make_unique<B>(); // Pointer to B object.
array_of_a[1] = make_unique<B>(); // Pointer to B object.
array_of_a[2] = make_unique<A>(); // Pointer to A object.
for (int i = 0; i < 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_cast<B*>(my_a);
if (my_b != nullptr)
my_b->methodSpecificToB();
else
std::cerr << " Object is not B type" << std::endl;
}
Объектный Паскаль, Делфи
[ редактировать ]В Object Pascal и Delphi оператор is
используется для проверки типа класса во время выполнения. Он проверяет принадлежность объекта к заданному классу, включая классы отдельных предков, присутствующих в дереве иерархии наследования (например, Button1 — это класс TButton , имеющий предков: TWinControl → TControl → TComponent → 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;
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Сан Микросистемс (2000). «Идентификация типа среды выполнения» . Руководство по программированию на C++ . Оракул . Проверено 16 апреля 2015 г.
- ^ «Библиотека языковой поддержки [support.rtti]» . угорь.есть . Проверено 13 июля 2021 г.
- ^ «Объектно-ориентированное программирование» . Learn.adacore.com . Проверено 13 июля 2021 г.
- ^ Бьерн Страуструп (март 1993 г.). «История C++: 1979–1991» (PDF) . Бьерн Страуструп. п. 50 . Проверено 18 мая 2009 г.
- ^ «Работа с RTTI — RAD Studio» . docwiki.embarcadero.com . Проверено 6 июня 2021 г.
- ^ Инглиш, Джон (22 февраля 2002 г.). «Глава 15». Ада 95: Искусство объектно-ориентированного программирования . Проверено 13 июля 2021 г.
- ^ «Избежание RTTI и поддержка -fno-rtti в Arm Compiler 6» . Разработчик рук . Проверено 13 июля 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 /документы/С++2003std.pdf
- ^ «ClassCastException (платформа Java SE 8)» .