Класс type_info
Точное определение класса type_info
зависит от реализации, но некоторые его характерные черты остаются неизменными в любой программе на C++:
class type_info { // ïðåäñòàâëåíèå çàâèñèò îò ðåàëèçàöèè private: type_info( const type_info& ); type_info& operator= ( const type_info& ); public: virtual ~type_info(); int operator==( const type_info& ); int operator!=( const type_info& ); const char * name() const; |
};
Поскольку копирующие конструктор и оператор присваивания – закрытые члены класса type_info, то пользователь не может создать его объекты в своей программе:
#include <typeinfo> type_info t1; // îøèáêà: íåò êîíñòðóêòîðà ïî óìîë÷àíèþ // îøèáêà: êîïèðóþùèé êîíñòðóêòîð çàêðûò |
type_info t2 (typeid( unsigned int ) );
Единственный способ создать объект класса type_info – воспользоваться оператором typeid.
В классе определены также операторы сравнения. Они позволяют сравнивать два объекта type_info, а следовательно, и результаты, возвращенные двумя операторами typeid. (Мы говорили об этом в предыдущем подразделе.)
typeid( re ) == typeid( manager ) // èñòèííî |
typeid( *pe ) != typeid( employee ) // ложно
Функция name()
возвращает C-строку с именем типа, представленного объектом type_info. Этой функцией можно пользоваться в программах следующим образом:
#include <typeinfo> int main() { employee *pe = new manager; // ïå÷àòàåò: "manager" cout << typeid( *pe ).name() << endl; |
Для работы с функцией-членом name()
нужно включить заголовочный файл <typeinfo>.
Имя типа – это единственная информация, которая гарантированно возвращается всеми реализациями C++, при этом используется функция-член name() класса type_info. В начале этого раздела упоминалось, что поддержка RTTI зависит от реализации и иногда в классе type_info
бывают дополнительные функции-члены. Чтобы узнать, каким образом обеспечивается поддержка RTTI в вашем компиляторе, обратитесь к справочному руководству по нему. Кроме того, можно получить любую информацию, которую компилятор знает о типе, например:
· список функций-членов класса;
· способ размещения объекта в памяти, т.е. взаимное расположение подобъектов базового и производных классов.
Одним из способов расширения поддержки RTTI является включение дополнительной информации в класс, производный от type_info. Поскольку в классе type_info
есть виртуальный деструктор, то оператор dynamic_cast позволяет выяснить, имеется ли некоторое конкретное расширение RTTI. Предположим, что некоторый компилятор предоставляет расширенную поддержку RTTI посредством класса extended_type_info, производного от type_info. С помощью оператора dynamic_cast
программа может узнать, принадлежит ли объект типа type_info, возвращенный оператором typeid, к типу extended_type_info. Если да, то пользоваться расширенной поддержкой RTTI разрешено.
#include <typeinfo> // Файл typeinfo содержит определение типа extended_type_info void func( employee* p ) { // понижающее приведение типа type_info* к extended_type_info* if ( eti *eti_p = dynamic_cast<eti *>( &typeid( *p ) ) ) { // если dynamic_cast завершается успешно, // можно пользоваться информацией из extended_type_info через eti_p } else { // если dynamic_cast завершается неудачно, // можно пользоваться только стандартным type_info } |
}
Если dynamic_cast
завершается успешно, то оператор typeid вернет объект класса extended_type_info, т.е. компилятор обеспечивает расширенную поддержку RTTI, чем программа может воспользоваться. В противном случае допустимы только базовые средства RTTI.
Упражнение 19.1
Дана иерархия классов, в которой у каждого класса есть конструктор по умолчанию и виртуальный деструктор:
class X { ... }; class A { ... }; class B : public A { ... }; class C : public B { ... }; |
Какие из данных операторов dynamic_cast
завершатся неудачно?
(a) D *pd = new D; |
(b) A *pa = new C; |
(c) B *pb = new B; |
(d) A *pa = new D; |
Упражнение 19.2
Объясните, когда нужно пользоваться оператором dynamic_cast вместо виртуальной функции?
Упражнение 19.3
Пользуясь иерархией классов из упражнения 19.1, перепишите следующий фрагмент так, чтобы в нем использовался ссылочный вариант dynamic_cast для преобразования *pa в тип D&:
if ( D *pd = dynamic_cast< D* >( pa ) ) { // использовать члены D } else { // использовать члены A |
Упражнение 19.4
Дана иерархия классов, в которой у каждого класса есть конструктор по умолчанию и виртуальный деструктор:
class X { ... }; class A { ... }; class B : public A { ... }; class C : public B { ... }; |
Какое имя типа будет напечатано в каждом из следующих случаев:
(a) A *pa = new D; cout << typeid( pa ).name() << endl; (b) X *px = new D; cout << typeid( *px ).name() << endl; (c) C obj; A& ra = cobj; cout << typeid( &ra ).name() << endl; (d) X *px = new D; A& ra = *px; |