* Имя типа – это единственная информация, которая гарантированно возвращается всеми реализациями 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_casteti *( &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 { ... };
class D : public X, public C { ... };
Какие из данных операторов dynamic_cast завершатся неудачно?
(a) D *pd = new D;
A *pa = dynamic_cast A* ( pd );
(b) A *pa = new C;
C *pc = dynamic_cast C* ( pa );
(c) B *pb = new B;
D *pd = dynamic_cast D* ( pb );
(d) A *pa = new D;
X *px = dynamic_cast X* ( pa );
Упражнение 19.2
Объясните, когда нужно пользоваться оператором dynamic_cast вместо виртуальной функции?
Упражнение 19.3
Пользуясь иерархией классов из упражнения 19.1, перепишите следующий фрагмент так, чтобы в нем использовался ссылочный вариант dynamic_cast для преобразования *pa в тип D&:
if ( D *pd = dynamic_cast( pa ) ) {
// использовать члены D
}
else {
// использовать члены A
}
Упражнение 19.4
Дана иерархия классов, в которой у каждого класса есть конструктор по умолчанию и виртуальный деструктор:
class X { ... };
class A { ... };
class B : public A { ... };
class C : public B { ... };
class D : public X, public C { ... };
Какое имя типа будет напечатано в каждом из следующих случаев:
(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;
cout typeid( ra ).name() endl;
19.2. Исключения и наследование