Проблема связывания сообщений и методов. Виртуальные методы
Проблема обращения полиморфизма. RTTI
Некоторые фрагменты из лекции:
Понятие полиморфизма: Полиморфизм в языке программирования означает многозначность переменных и функций.
Формы полиморфизма:
Полиморфной называется такая переменная, которая может хранить в себе значения различных типов данных
Полиморфной функцией является такая функция, которая может вызываться с аргументами различного типа, а фактический выполняемый код зависит от типа аргументов
Преимущества использования полиморфизма:
Полиморфизм позволяет записывать алгоритмы лишь однажды и затем повторно их использовать для различных типов данных, которые, возможно, еще не существуют (обобщенные действия или алго- ритмы).
Полиморфизм сужает концептуальное пространство, т.е. уменьшает количество информации, которое необходимо помнить программисту.
Пример обобщенного алгоритма:
Пусть имеется класс Enumerable, в котором объявлены операции отношения >, <, >=, <=, == и !=, и имеется свободная функция sort(), которая упорядочивает элементы массива типа Enumerable по возрастанию
Тогда всем классам, производным от класса Enumerable, будет доступна операция упорядочивания массива по возрастанию
Пример обобщенного алгоритма:
// Упорядочивание массива методом пузырька void sort(Enumerable *mass, int count) { for(int i = 0; i < count-1; i++) { for(int j = i+1; j < count; j++) { if(mass[i] > mass[j]) { swap(mass[i], mass[j]); } } } }
Пример сужения концептуального пространства:
Наличие операций == и != для классов QDate, QTime и QString позволяет одинаково работать с экземплярами этих классов в условных выражениях, не особо задумываясь над синтаксисом операций.
Одной из наиболее интересных особенностей объектно-ориентированных языков программирования является тот факт, что фактический тип переменной может не совпадать с типом, заявленным при ее описании.
Полиморфная переменная не только хранит данные фактического типа, но и позволяет использовать методы фактического типа.
Для обозначения типа, присвоенного переменной при ее описании, будем использовать термин «статический тип»
Термин «динамический тип» характеризует тип фактического значения.
Переменная, для которой динамический тип не совпадает (точнее, может не совпадать) со статическим, называется полиморфной.
Реализация полиморфных переменных в языке Си++:
В языке Си++ полиморфными могут быть только указатели и ссылки
Полиморфизм возникает, когда указателю (или ссылке) базового класса присваивается указатель на производный класс
Законность использования свойств и методов динамического типа определяется компилятором на основе статического типа
Преобразование статического типа к динамическому в языке Си++:
Для приведения статического типа к динамическому рекомендуется использовать два оператора языка Си++ из системы RTTI (про RTTI будет сказано позже):
dynamic_cast <type-id> (expression)
static_cast <type-id> (expression)
Главное достоинство указанных операторов заключается в том, что они возвращают NULL (для указателей), если приведение невозможно.
Понятие полиморфного контейнера
Среди полиморфных переменных можно выделить полиморфные контейнеры.
Полиморфным называется контейнер, при объявлении которого используется статический тип, а хранятся элементы динамического типа.
Проблемы, связанные с полиморфными переменными:
Проблема связывания сообщений и методов.
Переменная может рассматриваться как с точки зрения ее описания (т.е. статически), так и с точки зрения ее текущего значения (т.е. динамически).
Это различие становится важным, когда встречается метод, который определен в родительском классе и переопределен в дочернем, т.е. имеется одно сообщение и несколько адекватных ему методов.
Чаще всего мы ожидаем, что связывание сообщения должно происходить исходя из динамического типа данных (как в примере с графическими примитивами).
Однако существуют ситуации, где противоположный подход также полезен.
Проблема обращения полиморфизма.
Чаще всего, данная проблема возникает в связи с использованием полиморфных контейнеров.
Мы помещаем в полиморфный контейнер разнотипные объекты (например, различные примитивы), однако при извлечении объекта не можем сказать, какой перед нами объект и какими специфическими свойствами и специфическим поведением он обладает.
Решение проблемы обращения полиморфизма в языке Си++: RTTI
В Си++ имеются средства для определения динамического типа переменной.
Они образуют систему RTTI (Runtime Type Identification − идентификация типа во время выполнения)
В системе RTTI каждый класс имеет связанную с ним структуру типа type_info, которая кодирует раз- личную информацию о классе.
Для получения структуры type_info используется оператор typeid, который имеет следующий синтаксис:
typeid(type-id)
typeid(expression)
Связывание сообщений и методов в языке Си++:
В языке Си++ для обычных переменных (не указателей или ссылок) связывание методов осуществляется всегда статически, т.к. они не являются полиморфными.
Но когда объекты обозначаются с помощью указателей или ссылок используется динамическое связывание при условии, что метод объявлен как виртуальный.
Динамическое связывание в языке Си++ и виртуальные методы:
Метод, который объявлен с ключевым словом virtual, называется виртуальным
Если он объявлен именно так, то поиск метода начинается с динамического типа, если нет − со статического
Тип является полиморфным, если в нем имеются виртуальные методы
Рекомендации по использованию полиморфных переменных в языке Си++:
Если производный класс не расширяет базовый класс, то нет необходимости в приведении типов
Если же через полиморфную переменную требуется обратиться к дополнительным свойствам и методам производного класса, то необходимо явное приведение типа с помощью операции dynamic_cast
Если существует несколько производных классов со своим набором уникальных свойств и методов, то предварительно необходимо распознать класс с помощью метода typeid::name()