Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 [ 98 ] 99 100 101 102 103 104 105

ском типов, элементами которого в свою очередь являются списки типов. Это вполне реально, поскольку списки типов также являются типами. Ниже приведен оператор typedef, определяющий список типов, состоящий из трех списков типов, который можно применить для тройной диспетчеризации. Интересно, что возникающий в результате список действительно легко прочесть.

typedef TYPELIST 3

TYPELIST 3(shape. Rectangle, Ellipse), TYPELIST 3(Screen, Printer, plotter), TYPELlST 3(File, Socket, Memory)

ListofLists;

Диспетчер, управляемый ассоциативным массивом, можно преобразовать в диспетчер, управляемый вектором объектов типа std: :type info (в отличие от типа std:: pai г). Размер этого вектора равен количеству объектов, вовлеченных в множественную диспетчеризацию. Объявление этого класса может выглядеть следующим образом.

template

<

class ListofTypes, typename ResultType, typename CallbackType

>

class GeneralBasicDispatcher;

Шаблонный параметр ListofTypes представляет собой список типов, содержащий базовые типы, вовлеченные в множественную диспетчеризацию. Например, при штриховке пересечения двух фигур можно было бы применить список typelist 2 (shape, shape).

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

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

В настоящий момент библиотека Loki реализует лишь двойную диспетчеризацию. Ее обобщения читатели могут попробовать осуществить самостоятельно:

11.13. Резюме

Мультиметоды - это обобщенные виртуальные функции. Система поддержки выполнения программ на языке С-1-I- реализует диспетчеризацию виртуальных функций по одному классу, а мультиметоды осуществляют множественную диспетчеризацию в зависимости от нескольких классов одновременно. Это позволяет реализовать виртуальные функции для наборов типов, а не для отдельного типа.

Мультиметоды лучше всего реализовывать как свойство языка. В языке С-1-I- таких средств нет, но есть несколько способов, позволяющих использовать мультиметоды в виде библиотек.

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



обработки столкновений или пересечений полиморфных объектов, а также отображения объектов разными устройствами.

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

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

• Диспетчер, управляемый ассоциативньш массивом. Он использует ассоциативный массив, ключами которого являются объекты класса std: :type info. В этом массиве хранятся обратные вызовы (либо указатели на функции, либо функторы). Алгоритм распознавания типа выполняет бинарный поиск.

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

На основе указанных выше диспетчеров можно реализовать следующие функциональные возможности.

• Автоматизированное преобразование типов. (Не путайте с автоматическим преобразованием типов.) Для вызова диспетчеров необходимо выполнить приведение базовых типов к производным. Для этого предназначены трамплинные функции.

• Симметрия. Некоторые варианты использования двойной диспетчеризации являются симметричными по своей природе. Они применяются к одинаковым типам, и порядок следования параметров для них не важен. Например, обработчику столкновений все равно - ракета столкнулась с космическим кораблем или наоборот - его реакция будет одинаковой. Реализация симметрии в библиотеке позволяет уменьшить размер клиентского кода и избежать многих ошибок.

Статический диспетчер, основанный на грубом подходе, непосредственно поддерживает эти возможности, поскольку ему доступна обширная информация о типах. Остальные диспетчеры используют разные методы и добавляют новые уровни иерархии для реализации автоматизированного преобразования типов и поддержки симметрии. Двойные диспетчеры функций отличаются от двойных диспетчеров функторов. Они более эффективны.

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



Таблица 11.2. Сравнение разных реализаций двойной диспетчеризации

Статическая

диспетчеризация

(класс

StaticDispatcher)

Логарифмическая

диспетчеризация

(класс

BasicDispatcher)

Диспетчеризация с постоянным временем (класс BasicFastDispatcher)

Скорость для нескольких классов

Самая высокая

Средняя

Хорощая

Скорость для многих классов

Низкая

Хорощая

Самая высокая

Зависимость между классами

Сильная

Слабая

Слабая

Переключение между Нет

существующими

методами

В каждый класс добавляется макрос

Безопасность компиляции

Самая высокая

Хорощая

Хорощая

Безопасность выполнения

Самая высокая

Хорощая

Хорощая

11.14. Краткий обзор двойных диспетчеров

• в библиотеке Lold определены три вида двойных диспетчеров: классы StaticDispatcher, BasicDispatcher и BasicFastDispatcher.

• Объявление класса StaticDispatcher

template <

class Executor,

class BaseLhs,

class TypesLhs,

class BaseRhs = BaseLhs,

class TypesRhs = TypesLhs,

typename ResultType = void

>

class StaticDispatcher; Класс BaseLhs - это базовый тип левого операнда.

Класс TypesLhs представляет собой список типов, содержащий набор конкретных типов, вовлеченных в двойную диспетчеризацию в зависимости от левого операнда.

Класс BaseRhs - это базовый тип правого операнда.

Класс TypesRhs представляет собой список типов, содержащий набор конкретных типов, вовлеченных в двойную диспетчеризацию в зависимости от правого операнда.

Класс Executor содержит функции, вызываемые после распознавания типов. Он должен содержать перегруженную функцию-член Fi re для каждой комбинации типов, указанных в списках типов TypesLhs и TypesRhs.

Класс ResultType определяет тип значений, возвращаемых перегруженными функциями Executor:: Fi ге. Эти значения передаются дальще в качестве результата работы функции StaticDispatcher::Go.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 [ 98 ] 99 100 101 102 103 104 105