Язык программирования C++. Вводный курс

Оператор typeid


Второй оператор, входящий в состав RTTI, – это typeid, который позволяет выяснить фактический тип выражения. Если оно принадлежит типу класса и этот класс содержит хотя бы одну виртуальную функцию-член, то ответ может и не совпадать с типом самого выражения. Так, если выражение является ссылкой на базовый класс, то typeid

сообщает тип производного класса объекта:

#include <typeinfo>

programmer pobj;

employee &re = pobj;

// ñ ôóíêöèåé name() ìû ïîçíàêîìèìñÿ â ïîäðàçäåëå, ïîñâÿùåííîì type_info

// îíà âîçâðàùàåò C-ñòðîêó "programmer"

coiut << typeid( re ).name() << endl;



Операнд re

оператора typeid

имеет тип employee. Но так как re – это ссылка на тип класса с виртуальными функциями, то typeid

говорит, что тип адресуемого объекта – programmer (а не employee, на который ссылается re). Программа, использующая такой оператор, должна включать заголовочный файл <typeinfo>, что мы и сделали в этом примере.

Где применяется typeid? В сложных системах разработки, например при построении отладчиков, а также при использовании устойчивых объектов, извлеченных из базы данных. В таких системах необходимо знать фактический тип объекта, которым программа манипулирует с помощью указателя или ссылки на базовый класс, например для получения списка его свойств во время сеанса работы с отладчиком или для правильного сохранения или извлечения объекта из базы данных. Оператор typeid

допустимо использовать с выражениями и именами любых типов. Например, его операндами могут быть выражения встроенных типов и константы. Если операнд не принадлежит к типу класса, то typeid просто возвращает его тип:




int iobj;

cout << typeid( iobj ).name() << endl;   // ïå÷àòàåòñÿ: int
cout << typeid( 8.16 ).name() << endl;   // печатается: double

Если операнд имеет тип класса, в котором нет виртуальных функций, то typeid

возвращает тип операнда, а не связанного с ним объекта:



class Base {  /* нет виртуальных функций */ };

class Derived : public Base { /* íåò âèðòóàëüíûõ ôóíêöèé */ };

Derived dobj;

Base *pb = &dobj;

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* ) )  // èñòèííî

   // ÷òî-òî ñäåëàòü

/*

if ( typeid( pe ) == typeid( manager* ) )   // ëîæíî

if ( typeid( pe ) == typeid( employee ) )   // ëîæíî

if ( typeid( pe ) == typeid( manager )  )   // ëîæíî
*/

Условие в инструкции 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. (В следующем подразделе мы подробно рассмотрим этот интерфейс.)


Содержание раздела