3. Динамическая идентификация типа RTTI Одно из проявлений полиморфизма в Си++ – виртуальные функции в подклассах, которые можно вызывать через указатели на родительские классы. При таком подходе указатель родительского класса может использоваться либо для указания на объект родительского класса, либо для указания на объект любого подкласса, унаследованного от этого родительского класса. На этапе компиляции нельзя узнать, на объект какого класса указывает подобный указатель. Средства динамической идентификации типа (Run-Time Type Information, RTTI) позволяют определить тип объекта во время выполнения программы. Возможности RTTI используются довольно редко, но о них полезно иметь представление, чтобы лучше понимать свойства полиморфизма.
Для получения информации о типе объекта предназначен оператор typeid. Чтобы использовать его, в программу надо включить заголовочный файл typeinfo.h. Оператор typeid допускает две формы использования:
typeid(obj)
typeid(typename)
Здесь obj – этот тот объект, информацию о типе которого необходимое получить. Параметр typename – имя типа, информацию о котором надо получить (например, int или double).
Оператор typeid возвращает ссылку на объект класса type_info, который предназначен для хранения информации о типе. В классе type_info наиболее часто используемыми членами являются:
boot operator==(const type_info& obj);
boot operator!=(const type_info& obj);
const char* name();
Перегруженные операторы == и != применяются для сравнения типов объектов. В операторах сравнения обычно совместно используются обе формы оператора typeid. Функция name() возвращает указатель на строку с именем типа.
Оператор typeid позволяет получать типы разных объектов, но он наиболее полезен в случаях, когда в качестве его аргумента задается указатель полиморфного родительского класса. Тогда оператор автоматически возвращает тип реального объекта, на который указывает указатель. Этим объектом может быть как объект родительского класса, так и объект любого его подкласса. То же самое относится и к ссылкам.
Применение оператора typeid демонстрируется в программе 8.3. Сначала с помощью этого оператора мы получаем информацию об одном из встроенных типов данных Си++ (типе int). Затем оператор typeid дает возможность вывести на экран типы объектов, на которые указывает указатель р, являющийся указателем родительского класса CBaseClass. #include
#include class CBaseClass {
virtual void f() {;}
};
class CDerivedl: public CBaseClass {
virtual void f() {;}
};
class CDerived2: public CBaseClass {
virtual void f() {;}
}; void main()
{
CBaseClass *p, base_obj;
CDerivedl obj1;
CDerived2 obj2;
int i = 5; cout << "Тип переменной i - это " << typeid(i).name() << '\n'; p = &base_obj;
cout << "р указывает на объект типа " << typeid(*p).name() << '\n'; p = &obj1;
cout << "р указывает на объект типа " << typeid(*p).name() << '\n'; p = &obj2;
cout << "р указывает на объект типа " << typeid(*p).name() << '\n';
} Программа 8.3. Использование оператора typeid. Программа 8.3 выводит на экран следующие сообщения:
Тип переменной i – это int
р указывает на объект типа class CBaseClass
р указывает на объект типа class CDerivedl
р указывает на объект типа class CDerived2
4. Динамическое преобразование типа данных В Си++ продолжают поддерживаться характерные для языка Си операторы явного приведения типов данных (например, (CDerived2*)base_obj), но вместе с тем имеется несколько новых. Это операторы dynamic_cast, const_cast, reinterpret_cast и static_cast. Из них чаще всего используется оператор dynamic_cast.
Оператор dynamic_cast выполняет приведение типов в динамическом режиме, что позволяет контролировать правильность этой операции во время работы программы. У оператора dynamic_cast следующий синтаксис:
dynamic_саst<целевой тип>(выражение)
Здесь целевой_тип – это тип, к которому надо привести параметр выражение. И выражение, и целевой тип могут быть указателями или ссылками. Таким образом, оператор dynamic_cast используется для приведения типа одного указателя к типу другого или типа одной ссылки к типу другой.
Основное назначение оператора dynamic_cast заключается в реализации операции приведения полиморфных типов. Например, пусть дано два полиморфных класса В и D, причем класс D является производным от класса В. Тогда оператор dynamic_cast всегда может привести тип указателя D* к типу указателя В*. Это возможно потому, что указатель базового класса всегда может указывать на объект производного класса. Оператор dynamic_cast может также привести тип указателя В* к типу указателя D*, но только в том случае, если объект, на который указывает указатель, действительно является объектом типа D.
Оператор dynamic_cast выполняется успешно, когда указатель (или ссылка) после приведения типов становится указателем (или ссылкой) либо на объект целевого типа, либо на объект производного от целевого типа. В противном случае приведения типов не происходит. При неудачной попытке приведения типов результатом выполнения оператора dynamic_cast является нулевой указатель, если в операции использовались указатели. Если же в операции использовались ссылки, возбуждается исключительная ситуация класса bad_cast.
Применение оператора dynamic_cast показано в программе 8.4. #include class CBase {
public:
virtual void f() { cout << "Внутри класса CBase\n"; }
}; class CDerived: public CBase {
public:
void f() { cout << "Внутри класса CDerived\n"; }
}; void main()
{
CBase *bp, b_obj;
CDerived *dp, d_obj; dp = dynamic_cast(&d_obj);
if (dp)
{
cout << "Тип CDerived* к типу CDerived* приведен успешно\n";
dp->f();
}
else
cout << "Ошибка\n";
cout << '\n'; bp = dynamic_cast(&d_obj);
if (bp)
{
cout << "Тип CDerived* к типу CBase* приведен успешно\n";
bp->f();
}
else
cout << "Ошибка\n";
cout << '\n'; bp = dynamic_cast(&b_obj);
if (bp)
{
cout << "Тип CBase* к типу CBase* приведен успешно\n";
bp->f();
}
else
cout << "Ошибка\n";
cout << '\n'; dp = dynamic_cast(&b_obj) ;
if (dp)
cout << "Ошибка\n";
else
cout << "Тип CBase* к типу CDerived* не приведен\n";
cout << '\n'; bp = &d_obj; // bp указывает на объект класса CDerived
dp = dynamic_cast(bp);
if (dp)
{
cout << "Указатель bp к типу CDerived* приведен успешно, т.к. bp в "
"действительности указывает на объект типа CDerived\n";
dp->f() ;
}
else
cout << "Ошибка\n";
cout << '\n'; bp = &b_obj; // bp указывает на объект класса CBase
dp = dynamic_cast(bp);
if (dp)
cout << "Ошибка\n";
else
cout << "Указатель Ьр к типу CDerived* не приведен, т.к. Ьр в "
"действительности указывает на объект типа CBase\n";
cout << '\n'; dp = &d_obj; // dp указывает на объект типа CDerived
bp = dynamic_cast(dp);
if (bp)
{
cout << "Указатель dp к типу CBase* приведен успешно\n";
bp->f();
}
else
cout << "Ошибка\n";
}
Программа 8.4. Использование оператора динамического преобразования типа. Программа 8.4 выводит на экран следующие сообщения:
Тип CDerived* к типу CDerived* приведен успешно
Внутри класса CDerived Тип CDerived* к типу CBase* приведен успешно
Внутри класса CDerived Тип CBase* к типу CBase* приведен успешно
Внутри класса CBase Тип CBase* к типу CDerived* не приведен Указатель bp к типу CDerived* приведен успешно, т.к. bp в
действительности указывает на объект типа CDerived
Внутри класса CDerived Указатель Ьр к типу CDerived* не приведен, т.к. Ьр в
действительности указывает на объект типа CBase Указатель dp к типу CBase* приведен успешно
Внутри класса CDerived
|