Informații despre tipul de rulare - Run-time type information
În programarea computerului, informațiile despre tipul de execuție sau identificarea tipului de execuție ( RTTI ) sunt o caracteristică a unor limbaje de programare (cum ar fi C ++ , Object Pascal și Ada ) care expune informații despre tipul de date al unui obiect în timpul rulării . Informațiile despre tipul de rulare pot fi disponibile pentru toate tipurile sau numai pentru tipurile care le au în mod explicit (cum este cazul cu Ada). Informațiile de tip run-time sunt o specializare a unui concept mai general numit introspecție de tip .
În designul original C ++, Bjarne Stroustrup nu a inclus informații de tip run-time, deoarece el credea că acest mecanism a fost adesea folosit în mod greșit.
Prezentare generală
În C ++, RTTI poate fi utilizat pentru a face tipografii sigure , folosind dynamic_cast<>operatorul și pentru a manipula informațiile de tip în timpul rulării, folosind typeidoperatorul și std::type_infoclasa. În Object Pascal, RTTI poate fi utilizat pentru a efectua proiecții de tip sigur cu asoperatorul, pentru a testa clasa căreia îi aparține un obiect cu isoperatorul și pentru a manipula informațiile de tip în timpul rulării cu clase conținute în RTTIunitate (de exemplu, clase: TRttiContext , TRttiInstanceType , etc.). În Ada, obiectele de tipuri etichetate stochează și o etichetă de tip, care permite identificarea tipului acestor obiecte în timpul rulării. inOperatorul poate fi folosit pentru testare, în timpul rulării, în cazul în care un obiect este de un anumit tip și poate fi convertit în condiții de siguranță pentru ea.
RTTI este disponibil numai pentru clasele care sunt polimorfe , ceea ce înseamnă că au cel puțin o metodă virtuală . În practică, aceasta nu este o limitare deoarece clasele de bază trebuie să aibă un destructor virtual pentru a permite obiectelor din clasele derivate să efectueze o curățare corectă dacă sunt șterse dintr-un pointer de bază.
Unele compilatoare au semnalizatoare pentru a dezactiva RTTI. Utilizarea acestor semnalizatoare poate reduce dimensiunea generală a aplicației, făcându-le deosebit de utile atunci când vizează sisteme cu o cantitate limitată de memorie.
C ++ - tipid
Cuvântul typeid cheie este utilizat pentru a determina clasa unui obiect în timpul rulării . Returnează o referință la std::type_infoobiect, care există până la sfârșitul programului. Utilizarea typeid, într-un context non-polimorf, este adesea preferată în situațiile în care doar informațiile despre clasă sunt necesare, deoarece este întotdeauna o procedură în timp constant, în timp ce poate fi necesar să traverseze rețeaua de derivare a clasei a argumentului său în timpul rulării. Unele aspecte ale obiectului returnat sunt definite de implementare, cum ar fi și nu pot fi bazate pe compilatoare pentru a fi consistente.
dynamic_cast<class_type>typeiddynamic_caststd::type_info::name()
Obiectele clasei std::bad_typeidsunt aruncate atunci când expresia pentru typeideste rezultatul aplicării operatorului unar * pe un pointer nul . Dacă o excepție este aruncată pentru alte argumente de referință nule, depinde de implementare. Cu alte cuvinte, pentru ca excepția să fie garantată, expresia trebuie să ia forma în typeid(*p)care peste orice expresie care rezultă într-un indicator nul.
Exemplu
#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.
}
Ieșire (ieșirea exactă variază în funcție de sistem și compilator):
Person Employee Person* Employee Employee
C ++ - casting_dinamic și distribuție Java
dynamic_castOperatorul în C ++ este utilizat pentru downcasting referință sau pointer la un anumit tip mai în ierarhie de clase . Spre deosebire de static_cast, ținta dynamic_casttrebuie să fie un pointer sau o referință la clasă . Spre deosebire static_castși stilul C typecast (unde check tip este făcută în timpul compilării), o verificare de siguranță de tip se efectuează la rulare . Dacă tipurile nu sunt compatibile, se va arunca o excepție (atunci când se tratează referințe ) sau se va returna un pointer nul (atunci când se tratează cu pointeri ).
Un tipografiat Java se comportă similar; dacă obiectul java.lang.ClassCastExceptionproiectat nu este de fapt o instanță de tipul țintă și nu poate fi convertit într-unul printr-o metodă definită de limbă, va fi aruncată o instanță a .
Exemplu
Să presupunem că o funcție ia un obiect de tip Aca argument și dorește să efectueze o operație suplimentară dacă obiectul trecut este o instanță a B, o subclasă a A. Acest lucru poate fi realizat folosind dynamic_casturmătoarele.
#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]);
}
Ieșire consolă:
Method specific for B was invoked Method specific for B was invoked Exception std::bad_cast thrown. Object is not of type B
O versiune similară de MyFunctionpoate fi scrisă cu indicatori în loc de referințe :
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;
}
Delphi / Object Pascal
În Object Pascal, operatorul iseste utilizat pentru a verifica tipul unei clase în timpul rulării . Testează apartenența unui obiect la o anumită clasă, inclusiv clase de strămoși individuali prezenți în arborele ierarhiei moștenirii (de exemplu, Butonul 1 este o clasă TButton care are strămoși: TWinControl → TControl → TComponent → TPersistent → TObject , unde acesta din urmă este strămoșul din toate clasele). Operatorul aseste utilizat atunci când un obiect trebuie tratat în timpul rulării ca și cum ar aparține unei clase strămoșești.
Unitatea RTTI este utilizată pentru a manipula informațiile de tip obiect în timpul rulării. Această unitate conține un set de clase care vă permit să: obțineți informații despre clasa unui obiect și strămoșii săi, proprietăți, metode și evenimente, să modificați valorile proprietăților și metodele de apelare. Următorul exemplu arată utilizarea modulului RTTI pentru a obține informații despre clasa căreia îi aparține un obiect, crearea acestuia și pentru a apela metoda acestuia. Exemplul presupune că clasa TSubject a fost declarată într-o unitate numită 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;