19.1.2. Оператор typeid
19.1.2. Оператор typeid
Второй оператор, входящий в состав RTTI, – это typeid, который позволяет выяснить фактический тип выражения. Если оно принадлежит типу класса и этот класс содержит хотя бы одну виртуальную функцию-член, то ответ может и не совпадать с типом самого выражения. Так, если выражение является ссылкой на базовый класс, то typeid сообщает тип производного класса объекта:
#include typeinfo
programmer pobj;
employee &re = pobj;
// n ooieoeae name() iu iiciaeiieiny a iia?acaaea, iinayuaiiii type_info
// iia aica?auaao C-no?ieo "programmer"
coiut typeid( re ).name() endl;
Операнд re оператора typeid имеет тип employee. Но так как re – это ссылка на тип класса с виртуальными функциями, то typeid говорит, что тип адресуемого объекта – programmer (а не employee, на который ссылается re). Программа, использующая такой оператор, должна включать заголовочный файл , что мы и сделали в этом примере.
Где применяется typeid? В сложных системах разработки, например при построении отладчиков, а также при использовании устойчивых объектов, извлеченных из базы данных. В таких системах необходимо знать фактический тип объекта, которым программа манипулирует с помощью указателя или ссылки на базовый класс, например для получения списка его свойств во время сеанса работы с отладчиком или для правильного сохранения или извлечения объекта из базы данных. Оператор typeid допустимо использовать с выражениями и именами любых типов. Например, его операндами могут быть выражения встроенных типов и константы. Если операнд не принадлежит к типу класса, то typeid просто возвращает его тип:
int iobj;
cout typeid( iobj ).name() endl; // ia?aoaaony: int
cout typeid( 8.16 ).name() endl; // печатается: double
Если операнд имеет тип класса, в котором нет виртуальных функций, то typeid возвращает тип операнда, а не связанного с ним объекта:
class Base { /* нет виртуальных функций */ };
class Derived : public Base { /* iao ae?ooaeuiuo ooieoee */ };
Derived dobj;
Base *pb =
cout typeid( *pb ).name() endl; // печатается: Base
Операнд typeid имеет тип Base, т.е. тип выражения *pb. Поскольку в классе Base нет виртуальных функций, результатом typeid будет Base, хотя объект, на который указывает pb, имеет тип Derived.
Результаты, возвращенные оператором typeid, можно сравнивать. Например:
#include typeinfo
employee *pe = new manager;
employee& re = *pe;
if ( typeid( pe ) == typeid( employee* ) ) // enoeiii
// ?oi-oi naaeaou
/*
if ( typeid( pe ) == typeid( manager* ) ) // ei?ii
if ( typeid( pe ) == typeid( employee ) ) // ei?ii
if ( typeid( pe ) == typeid( manager ) ) // ei?ii
*/
Условие в инструкции if сравнивает результаты применения typeid к операнду, являющемуся выражением, и к операнду, являющемуся именем типа. Обратите внимание, что сравнение
typeid( pe ) == typeid( employee* )
возвращает истину. Это удивит пользователей, привыкших писать:
// вызов виртуальной функции
pe-salary();
что приводит к вызову виртуальной функции salary() из производного класса manager. Поведение typeid(pe) не подчиняется данному механизму. Это связано с тем, что pe – указатель, а для получения типа производного класса операндом typeid должен быть тип класса с виртуальными функциями. Выражение typeid(pe) возвращает тип pe, т.е. указатель на employee. Это значение совпадает со значением typeid(employee*), тогда как все остальные сравнения дают ложь.
Только при употреблении выражения *pe в качестве операнда typeid результат будет содержать тип объекта, на который указывает pe:
typeid( *pe ) == typeid( manager ) // истинно
typeid( *pe ) == typeid( employee ) // ложно
В этих сравнениях *pe – выражение типа класса, который имеет виртуальные функции, поэтому результатом применения typeid будет тип адресуемого операндом объекта manager.
Такой оператор можно использовать и со ссылками:
typeid( re ) == typeid( manager ) // истинно
typeid( re ) == typeid( employee ) // ложно
typeid( &re ) == typeid( employee* ) // истинно
typeid( &re ) == typeid( manager* ) // ложно
В первых двух сравнениях операнд re имеет тип класса с виртуальными функциями, поэтому результат применения typeid содержит тип объекта, на который ссылается re. В последних двух сравнениях операнд &re имеет тип указателя, следовательно, результатом будет тип самого операнда, т.е. employee*.
На самом деле оператор typeid возвращает объект класса типа type_info, который определен в заголовочном файле typeinfo. Интерфейс этого класса показывает, что можно делать с результатом, возвращенным typeid. (В следующем подразделе мы подробно рассмотрим этот интерфейс.)