Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C__lab_11.doc
Скачиваний:
3
Добавлен:
12.11.2019
Размер:
266.75 Кб
Скачать

Динамическая идентификация и приведение типов

         Языки программирования не поддерживающие ООП и полиморфизм не нуждаются в средствах идентификации типа на этапе выполнения. В С++ напротив это один из основных механизмов.

         В С++ полиморфизм реализуется через наследование виртуальных функций (методов) и указатели. Указатель на базовый класс в процессе выполнения приложения может ссылаться на объект любого производного класса. При этом, виртуальные функции будут вызываться корректно, т.е. будет вызвана виртуальная функция того класса, какого фактически класса объект, вызвавший через указатель виртуальную функцию. Но вызвать обычные методы так не удается. Указатель на базовый класс «знает» только о тех обычных методах, которые находятся в базовом классе. В такой ситуации необходимо приведение типа указателя на базовый класс к указателю на производный класс. Такое приведение типа вниз по иерархии классов является не безопасным, и в С++ разработаны более совершенные методики приведения типов, чем в С.

         Информация о типе объекта полиморфного класса (класса, содержащего виртуальные методы) может быть получена с помощью оператора typeid, для чего в программу нужно включить заголовочный файл        #include <typeinfo> и использовать выражение

type_info ob_info typeid(объект_полиморфного_класса);

Объект_полиморфного_класса – это тот объект, информацию о типе которого нужно получить; ob_info – объект класса type_info, в который оператор typeid возвращает информацию об объекте полиморфного типа. Если применять оператор typeid к не полиморфным объектам, то будет получена информация о базовом типе.

         В классе type_info определены операции сравнения «=» (равно) и «!=» (не равно) и метод без параметров name для получения имени типа. Следующая простая программа демонстрирует применение оператора typeid:

// Пример использования typeid

#include <iostream>

#include <typeinfo> // Без этого заголовочного файла typeid не работает

using namespace std;

class myclass { // Создается полиморфный класс

public:

        virtual void f(){}; // Наличие хотя бы одного виртуального метода делает //класс полиморфным

};

int main()

{

    int i=1; // Создаем переменную типа int

    cout << "typeid(i).name()="<<typeid(i).name()<<endl; // Определяем тип i

    cout << "typeid(2.7).name()="<<typeid(2.7).name()<<endl;// Определяем тип //числового литерала

    char ch='a'; // Создаем переменную типа char

    cout << "typeid(ch).name()="<<typeid(ch).name()<<endl; //Определяем тип ch

   cout << "typeid('b').name()="<<typeid('b').name()<<endl; // Определяем тип //символьного литерала

    myclass ob; // Создаем переменную типа myclass

    cout << "typeid(ob).name()="<<typeid(ob).name()<<endl;//Определяем тип ob

    int n;

    cin>>n;

    return 0;

}

         На рисунке показан вывод программы в окно консоли: все типы определены на этапе выполнения программы правильно.

Рисунок. Использование оператора typeid

        В следующей программе устанавливается соответствие или несоответствие типов:

// Использование операторов == и != с оператором typeid для проверки //совпадения типов

#include <iostream>

#include <typeinfo>

using namespace std;

class x { // Объявляется полиморфный класс

    virtual void f() {}

};

class y {// Объявляется другой полиморфный класс

    virtual void f() {}

};

int main()

{

    x x1, x2; // x1 и x2 – одного типа

    y y1; // х1 и у1 – разных типов

    if(typeid(x1) == typeid(x2))

        cout << " typeid(x1) == typeid(x2) \n";

    else

        cout << " typeid(x1) != typeid(x2) \n";

    if(typeid(x1) != typeid(y1))

        cout << " typeid(x1) != typeid(y1) \n";

    else

        cout << " typeid(x1) == typeid(y1) \n";

    return 0;

}

         Приведенное на рисунке окно вывода программы показывает, что программа осуществляет сравнение типов правильно:

Рисунок. Демонстрация сравнения типов во время выполнения программы

         В предыдущем примере использовалась информация о типе на этапе выполнения программы. Однако, часто бывает важно не только знать тип указателя, но и изменять его.

В следующем примере заданы два класса: класс А и производный от него класс В. Оба имеют не виртуальные методы (классы А и В имеют виртуальный метод, чтобы создать полиморфную цепочку классов). В программе показывается как через указатель на класс А вызвать обычный (не виртуальный) метод класса В с помощью операции приведения типа (виртуальные методы вызываются без проблем).

Рисунок. С помощью указателя на базовый класс нельзя вызвать метод производного класса

         На рисунке показано окно среды cbuilder с указанием на ошибку в программном коде. Хотя указатель poba является указателем на базовый класс А, но в программе он фактически указывает на объект класса В. Тем не менее, через него нельзя вызвать обычный метод класса В (обычный метод класса А вызывается без проблем). Виртуальный же метод вызывается правильно: из класса В. Применение динамического приведения типа позволяет решить проблему: вместо строки

poba->methodb();

используется другая строка кода

(dynamic_cast<b *>(poba))->methodb();

         В этой строке производится приведение (преобразование) указателя poba от типа А* к типу В*. Это коренным образом меняет ситуацию: теперь указатель poba «знает» метод methodb и вызывает его без проблем. На рисунке показан вывод в окно консоли.

#include <iostream>

using namespace std;

class a { // Объявление класса А

public:

        void methoda(){cout<<"methoda"<<endl;};

        virtual void f(){cout<<"virtual a->lf()"<<endl;};

        };

class b : public a { // Объявление класса В наследника класса А

public:

        void methodb(){cout<<"methodb"<<endl;};

        virtual void f(){cout<<"virtual b->lf()"<<endl;};

        };

int main(){

a *poba; // Создаем указатель на класса А

b  obb; // Создаем объект класса В

poba=&obb; // Теперь poba есть указатель на объект класса В

poba->methoda(); // Вызывается без проблем

poba->f();// Вызывается без проблем

(dynamic_cast<b *>(poba))->methodb(); // Теперь тоже вызывается

int n; cin>>n; // Задержка закрытия окна консоли

return 0;

}

Рисунок. Динамическое приведение типа

         Оператор приведения типа dynamic_cast имеет следующий формат:

dynamic_cast<указатель_на_целевой_класс>(указатель_на_базовый класс)

         В угловых скобках указывается тип указателя на тот класс, поле или метод (метод не виртуальный) которого нужно использовать. В круглых скобках обычно находится указатель на вершину полиморфной цепочки, который фактически указывает на некоторый отличный от целевого класса класс из полиморфной цепочки классов.

         Динамическое приведение типов бывает трех видов:

1.      «вверх» - указатель преобразуется к типу предка (безопасный вид преобразования);

2.      «вниз» - указатель преобразуется к типу потомка (не безопасный вид преобразования, возможен если преобразуемый указатель действительно указывает на объект целевого типа);

3.      «по горизонтали» - от одного типа к типу на том же уровне иерархии (не безопасный вид преобразования).

         Используя динамическое преобразование типов необходимо учитывать, что:

1.   используется только для полиморфных типов (у базового класса есть хотя бы один виртуальный метод);

2.   динамическое преобразование типов применяется к указателям и, точно так же , к ссылкам;

3.   при неудачной попытке преобразования типов результатом выполнения оператора dynamic_cast будет нулевой указатель, а в случае ссылок генерируется исключительная ситуация bad_cast.

Кроме оператора dynamic_cast используются также операторы const_cast, reinterpret_cast и static_cast. Оператор static_cast предназначен для приведения не полиморфных типов (в цепочке наследования нет виртуальных методов). Оператор reinterpret_cast дает возможность привести указатель одного типа к указателю совершенно другого типа. Оператор const_cast позволяет избавить объект некоторого класса, объявленный с модификатором const, от этого модификатора.

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