Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP.doc
Скачиваний:
6
Добавлен:
25.04.2019
Размер:
1.34 Mб
Скачать

2.5. Преобразование динамических типов. Динамическая идентификация типов

Благодаря полиморфизму в С++ возможна инициализация указателей и ссылок на базовые классы соответственно указателями и ссылками на объекты производных классов. В результате появляется возможность работать с компонентами производных классов через указатели и ссылки на объекты базовых классов. Однако, чтобы обратиться к какому-либо компоненту, нужно точно знать текущий динамический тип указателя или ссылки (поскольку неизвестно, например, есть ли этот компонент в текущем динамическом типе).

Уточнение динамического типа обеспечивает операция dynamic_cast. Формат ее использования следующий:

dynamic_cast<new_type>(expression)

где expression – выражение, для которого требуется уточнить динамический тип; new_type – новый тип выражения.

В качестве new_type в описанном формате может выступать ссылка или указатель (возможно, с модификаторами) на полностью определенный полиморфный класс. Также new_type может иметь тип void *, const void *, volatile void * или const volatile void *. В свою очередь, expression может представлять собой указатель или ссылку на полностью определенный полиморфный класс или же быть нулевым указателем.

Работа операции dynamic_cast подчиняется следующим правилам.

  1. Если new_type – это указатель на класс, то expression должно быть праводопустимым выражением типа «указатель на класс». Результат преобразования – значение выражения expression (праводопустимое выражение), приведенное к типу new_type.

  2. Если new_type – это ссылка на класс, то expression должно быть леводопустимым выражением типа «ссылка на класс». Результат преобразования – значение выражения expression (леводопустимое выражение), приведенное к типу new_type.

  3. При совпадении типов new_type и expression тип исходного выражения не меняется (если нет отличий в модификаторах; если new_type более квалифицирован модификаторами, то выполняется конвертация типа по модификаторам).

  4. Если выражение expression есть нулевой указатель, то результатом преобразования будет нулевой указатель типа new_type.

Операция dynamic_cast позволяет преобразовывать динамический тип в двух направлениях: от производного класса к базовому и от базового к производному. Если, например, указатель pBase на базовый класс CBase инициализировать адресом объекта d производного класса CDerived, указатель pBase будет связан с частью объекта d, соответствующей классу CBase (динамический тип адреса объекта как бы обобщается до CBase*). Если затем указатель pBase преобразовать к типу CDerived*, то он свяжется с адресом объекта d (динамический тип указателя будет уточнен до CDerived*).

Операцию dynamic_cast можно использовать и в иерархиях с множественным наследованием. Причем, если преобразование выполняется в направлении от производного класса к базовому, то результатом является ссылка или указатель на один из объектов базового класса, входящих в объект производного класса (если объектов базового класса в объекте производного класса несколько, то выбирается только один из них).

В определенных случаях операция dynamic_cast способна генерировать ошибки. Например, при попытке привести типы в разных ветвях иерархии классов, не связанных наследованием, или в случае закрытого наследования (когда объект базового класса недоступен). Проявлением ошибки в случае указателей будет нулевое значение результирующего указателя; в случае ссылки генерируется исключение std::bad_cast. Очевидно, что преобразование указателей более опасно, чем преобразование ссылок, поскольку ошибка с указателями в момент возникновения никак себя не проявляет (многие программисты с большой неохотой проверяют указатели на NULL перед их разыменованием) и сказывается лишь в следующих подвыражениях или операторах.

Приведенный ниже пример иллюстрирует описанные особенности использования операции dynamic_cast:

Пример

#include <typeinfo.h> // необходимо для std::bad_cast

class A {

public:

virtual void fA() {}

// делаем все классы полиморфными

};

class B: public A {

public:

void fB() {}

};

class C: public B {

public:

void fC() {}

};

class D: private B {

public:

void fD() {}

};

// использование классов

A * p = 0;

C c;

D d;

p = dynamic_cast<A*>(&c); // обобщение динамического типа &c

p->fA(); // так можно вызвать только функции класса A

dynamic_cast<B*>(p)->fB(); // уточнение динамического типа p

dynamic_cast<C*>(p)->fC(); // можно вызвать и fB, и fC

// p = dynamic_cast<A*>(&d); // так как D закрыто наследует от B

// p->fA(); // преобразование завершается с ошибкой (p == NULL)

try {

*p = dynamic_cast<A&>(d); // здесь будет исключение

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]