А. Александреску - Современное проектирование на C++ (1119444), страница 75
Текст из файла (страница 75)
Их описание привелено в библиотеке Еок1 в файле мц1гбветйодз. Ь. Таблица 11.1. Требования, предъявляемые к стратегии 01нратойвгваокепд Выражение Тнп возвращаемого Примечания значения сору, азз1дп, анар, дезтгоу Ьасйнпд.дбд<вовеьйз, 5овеяйв>(са11Ьасй) Семантика значений чо1д Добавляет обратный вызов в объект Ьасйнпд для типов 5оаеьйз и 5овеяйв Ьасйнпб.ао(вазеьйзб, вазеяйзб) яезц1ттуре Выполняет просмотр н диспетчеризацию лля двух объектов. Если обработчик ие найден, генерирует исключительную ситуацию зсд::гцпсцае еггог Ьасйнпд.яеаоче<5овеьйз, 5оаеяйв>О Ьоо1 Удаляет обратный вызов лля типов 5овеЬИз н 5овеяйз.
Если обратный вызов существовал, возвращает значение Сгце Если обратный вызов для типов 5овеьйз и 5овеяйз зарегистрирован, возвращает значение тгце. Новый обратный вызов не добавляется Ьасйнпд.напд1егех1зтз <5овесйз, 5оаеяйз>0 Ьоо1 11.12. Перспективы ° Статический диспетчер, управляемый двумя списками типов. ° Диспетчер, управляемый ассоциативным массивом, содержащим пары объектов типа зтб:: суре 1пго' в качестве ключей. ° Диспетчер, управляемый матрицей, индексированной уникальными идентификационными номерами классов.
Эти диспетчеры можно легко обобщить. Статический диспетчер, управляемый двумя списками типов, можно преобразовать в диспетчер, управляемый одним спи- г эти объекты скрыты под оболочкой класса огдегедтуретпео, облегчающего сравнение и копирование. Глава 11.
Мультиметоды Мы стоиги на пороге новых обобщений. Результаты, полученные при разработке двойного диспетчера, можно применить для реализации действительно обобщенной множественной диспетчеризации. На самом деле это довольно просто. Мы определили три типа диспетчеров. ском типов, элементами которого в свою очередь являются списки типов. Это вполне реально, поскольку списки типов также являются типами.
Ниже приведен оператор суредет, определяющий список типов, состоящий из трех списков типов, который можно применить для тройной диспетчеризации. Интересно, что возникающий в ре- зультате список действительно легко прочесть. суреоеУ туяеьсвт 3 ( турдьтзт 3(вйаре, яессапо1е, д11(рве), турдь|вт 3(зсгееп, ягбпсег, Р)оссег), турдьсвт 3(гз1е, воскес, меаогу) ) ьчздсотьтзсз; Диспетчер, управляемый ассоциативным массивом, можно преобразовать в диспетчер, управляемый вектором объектов типа зсд::суре зобо (в отличие от типа зсб::рабг).
Размер этого вектора равен количеству объектов, вовлеченных в множественную диспетче- ризацию. Объявление этого класса может выглядеть следующим образом. сеир1асе < с1азз ьззсоттурез, сурепаже яези1стуре, сурепаже са11Ьасйтуре > с1аьв оепега1ваззсо)зрассйег; Шаблонный параметр ьззсоФтурез представляет собой список типов, содержащий базовые типы, вовлеченные в множественную диспетчеризацию. Например, при штриховке пересечения двух фигур можно было бы применить список туркь|вт 2 свьаре, вьаре). Диспетчер, управляемый матрицей, можно обобщить с помощью многомерного массива, который можно построить на основе рекурсивного шаблонного класса.
Существующая схема присвоения идентификационных номеров остается неизменной. Следует отметить, что иерархию классов, используемую для двойной диспетчеризации, можно без изменения применять и для множественной диспетчеризации. Все эти возможные расширения требуют большого объема работы. Особенно трудная проблема, связанная с множественной диспетчеризацией, заключается в том, что в языке С++ нет универсального способа представления функций, имеющих переменное количество аргументов.
В настоящий момент библиотека Еой реализует лишь двойную диспетчеризацию. Ее обобщения читатели могут попробовать осуществить самостоятельно-. 11.13. Резюме Мультиметоды — это обобщенные виртуальные функции. Система поддержки выполнения программ на языке С++ реализует диспетчеризацию виртуальных функций по одному классу, а мультиметоды осуществляют множественную диспетчеризацию в зависимости от нескольких классов одновременно. Это позволяет реализовать виртуальные функции для наборов типов, а не для отдельного типа. Мультиметоды лучше всего реализовывать как свойство языка. В языке С++ таких средств нет, но есть несколько способов, позволяющих использовать мультиметоды в виде библиотек.
Мультиметоды нужны в приложениях, выполняюших те или иные алгоритмы в зависимости от типов одного или нескольких объектов. Обычно они применяются для Часть й. Компоненты 312 обработки столкновений или пересечений полиморфных объектов, а также отображения объектов разными устройствами. В этой главе мы ограничились двойной диспетчеризацией. Объект, выполняюший вызов соответствующей функзгии, называется двойным диспетчером. Существует несколько типов дипспетчеров. ° Статический диспетчер. Он полагается на информацию о статических типах (прелоставленную в виде списка типов) и выполняет линейный развернутый поиск соответствующего типа. Как только требуемый тип найден, диспетчер вызывает перегруженную функцию-член для обработки объекта.
° Диспетчер, улравляеиый ассочиалщвным массивом. Он использует ассоциативный массив, ключами которого являются объекты класса зМ::туре зобо. В этом массиве хранятся обратные вызовы (либо указатели на функции, либо функторы). Алгоритм распознавания типа выполняет бинарный поиск. ° Диспетчер, управляемый матрицей. Это самый быстрый диспетчер, однако для его применения нужно модифицировать классы (добавить макрос в каждый класс, к которому применяется диспетчеризация). Для такой диспетчеризации нужно выполнить два виртуальных вызова, набор числовых проверок и операцию доступа к элементу матрицы.
На основе указанных выше диспетчеров можно реализовать следующие функциональные возможности. ° Аваоматизированное преобразование типов. (Не пугайте с автоматическим преобразованием типов.) Для вызова диспетчеров необходимо выполни~ь приведение базовых типов к производным. Для этого предназначены трамплинные функции. ° Симметрия. Некоторые варианты использования двойной диспетчеризации являются симметричными по своей природе. Они применяются к одинаковым типам, и порядок следования параметров для них не важен. Например, обработчику столкновений все равно — ракета столкнулась с космическим кораблем илн наоборот — его реакция будет одинаковой. Реализация симметрии в библиотеке позволяет уменьшить размер клиентского кода и избежать многих ошибок.
Статический диспетчер, основанный на грубом подходе, непосредственно поддерживает эти возможности, поскольку ему доступна обширная информация о типах. Остальные диспетчеры используют разные методы и добавляют новые уровни иерархии лля реализации автоматизированного преобразования типов и поддержки симметрии. Двойные диспетчеры функций отличаются от лвойных лиспетчеров функторов. Они более эффективны. В табл. 11.2 приведены результаты сравнения разных диспетчеров, опрелеленных в этой главе. Очевидно, что ни один из них не идеален. Выбор оптимального диспетчера зависит от конкретной ситуации. Глава 11. Муньтиметоды Таблица 11.2.
Сравивнив разных рвализаций двойной диспвтчвризации Статическая диспетчеризация (класс БсабсВ!зрагспег) Логарифмическая диспетчеризация (класс Ваяс)))зрассаег) Диспетчеризация с постоянным временем (класс Вая)сРаяй)ЬраГспег) Самая высокая Средняя Скорость лля нескольких классов Хорошая Низкая Хорошая Самая высокая Скорость для многих классов Слабая Слабая Сильная Зависимость между классами В кажлый класс добавляется макрос Переключение между Нет существующими методами Нет Безопасность компиляции Самая высокая Хорошая Хорошая Хорошая Хорошая Самая высокая Безопасность выполнения 11.14.
Краткий обзор двойных диспетчеров класса всат(со)зратспег ° Объявление теир1асе < с1аз в с1азз с1азз с1азз с1ааа турец > с1аав вт ехеситог, вазеьцз, турезьпз, вазепцз = вазеьнз, турезяпз = турезьпз„ аие яези1ттуре = ио1д ат1со(зратсцег," Класс вазеьвз — зто базовый тип левого операнда. Класс турезьпз представляет собой список типов, содержащий набор конкретных типов, вовлеченных в двойную диспетчеризацию в зависимости от левого операнда. Класс вазепМ вЂ” зто базовый тип правого операнда. Класс турезяпз представляет собой список типов, содержащий набор конкрет- ных типов, вовлеченных в двойную диспетчеризацию в зависимости от правого операнда.
Класс ехеситог солержит функции, вызываемые после распознавания типов. Он должен содержать перегруженную функцию-член к)ге для каждой комби- нации типов, указанных в списках типов турезьпз и турезпоз. Класс пези1ттуре определяет тип значений, возвращаемых перегруженными функциями ехеситог:: Г1ге. Эти значения передаются дальше в качестве ре- зультата работы функции всат)со(зратспег:: ео. 314 Часть!1. Компоненты ° В библиотеке (.о)и определены три вида двойных диспетчеров: классы втат1со(зратспег, ваз1со(зратспег и ваз1сказто1зратсвег. ° Класс ехесцсог должен содержать перегруженную функцию-член опеггог(аазеЬЬМ, вазекбзв) для обработки ошибок.
Функция ехесисог::опеггог вызывается классом 5сас1со1зрассйе г при обнаружении неизвестного типа. ° Допустим, что классы кесСапд1е и Е111рзе являются потомками класса 5Ьаре, а классы Рг1псег и 5сгееп — потомки класса оцсрцсоеч1се. зсгцсс Ра1лсег ( Ьоо! г1ге(иессапд1ев, Рг1псегв); Ьоо1 г1ге(Е111рзев, Рг1псегб); Ьоо1 г1ге(йессапд1е6, 5сгеепв)", Ьоо1 гвге(Е111рзев, 5сгеепв); Ьоо! опеггог(5Ьарей„ оцсрцсоеч1сев); суредеФ 5СаС1со15рассйег < Ра1пСег, 5Ьаре, тчРЕЬтвт 2(кессапд1е, Е111рзе), оцсрцсоеч1се, тчгеьтвт 2(гг1псегб, 5сгееп), Ьоо1 > овзрассйег; ° Класс 5СаС)со1зрассбег реализует функцию-член по, получающую параметры, имеющие типы ВазеЬЬзй, вазекбзб и ехесцсогб соответственно.
Вот пример ее использования (в контексте предыдуших определений). о1зрассйег Нзр; 5Ьаре* р5Ь = ...; оисрцсоеч1се* роеч = ...; Ьоо1 геьц1с = д1зр.ао(*р5Ь, *роеч); ° Классы ваз1со1зрассЬег и ваз1сгавсовзрассбег реализуют динамические диспетчеры, позволяющие пользователям добавлять обрабатывающие функции в ходе выполнения программы. ° Класс ваз1со1зрассбег находит обработчик за логарифмическое время.
Класс ваз1сгазсо1зрассйег находит обработчик за постоянное время, однако для этого пользователь должен изменить определения всех классов, вовлеченных в диспетчеризацию. ° Оба класса имеют одинаковый интерфейс. севр1асе < с1азз вазеьбз, с1авв вазеибз = вазеьЬз, сурепаве кезц1стуре = чо1д, сурепаве са11Ьасктуре = кази!стуре (*) (вазеьбзв, вазеибзв) > с1азз ваавсо1зрассЬег; Здесь класс Са11Ьасктуре является типом объекта, выполняющего диспетчеризацию.