Опубликован: 17.10.2005 | Уровень: специалист | Доступ: свободно
Лекция 10:

Универсализация

Родовые классы

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

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

На первый взгляд эти требования кажутся несовместимыми, но на самом деле это не так. Первое требование заставляет нас объявить тип. Но вовсе не требуется, чтобы тип в объявлении был конкретным! Назвав имя типа, мы умиротворим механизм проверки. ("Назови свой страх - и он уйдет"). В этом идея универсализации: получить класс с параметром, задающим тип, снабдить его именем, назвав его формальным родовым параметром.

Объявление родового класса

По соглашению родовой параметр обычно, использует имя G (от Generic ). Это неформальное правило. Если нужны еще родовые параметры, они будут названы H, I и т.д.

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

indexing
      description: "Стек элементов произвольного класса G"
class STACK [G] feature
      count: INTEGER
            -- Количество элементов в стеке
      empty: BOOLEAN is
            -- Есть ли элементы?
         do ... end
      full: BOOLEAN is
            -- Стек заполнен?
         do ... end
      item: G is
            -- Вершина стека
         do ... end
      put (x: G) is
            -- Втолкнуть x в стек.
         do ... end
      remove is
            -- Вытолкнуть элемент из стека.
         do ... end
end -- class STACK

Формальный родовой параметр G можно использовать в объявлениях класса не только для результата функций (как в item ) и формальных аргументов подпрограмм (как в put ), но и для атрибутов и локальных сущностей класса.

Использование родового класса

Клиент может использовать родовой класс для объявления собственных сущностей, задающих стек. В этом случае в момент объявления следует задать фактический тип элементов стека - фактический родовой параметр, например:

sp: STACK [POINT]

Если у класса несколько родовых параметров, то соответственно столько же необходимо задать и фактических параметров.

Предоставление фактических родовых параметров родовому классу для создания типа называется родовым порождением (generic derivation), а полученный в результате класс, такой как STACK [POINT], называют параметрически порожденным классом.

Родовому порождению требуется тип, родовое порождение создает новый тип:

  • Результат порождения STACK [POINT] является типом.
  • Для получения такого результата, необходим уже существующий тип, используемый в качестве фактического параметра ( POINT в примере).

Фактический параметр может быть произвольным типом. Ничто не мешает выбрать тип, который сам по себе параметрически порожден. Предположим, что мы определили другой родовой класс LIST [G], тогда можно определить стек, элементы которого являются списками точек:

slp: STACK [LIST [POINT]]

или, используя STACK [POINT] как фактический родовой параметр, - стек стеков точек:

ssp: STACK [STACK [POINT]]

Нет предела глубины таких вложений, кроме естественной необходимости сохранять простоту программного текста.

Терминология

Обсуждая универсализацию, необходимо уточнить используемые термины.

  • Процесс порождения нового типа, такого как STACK [POINT], из типов POINT и STACK, можно было бы называть созданием экземпляра типа "generic instantiation". Но этот термин мог бы ввести в заблуждение, поскольку в названии неявно предполагается процесс периода выполнения ПО. Заметьте, родовое порождение - статический механизм, действующий на текст программы, а не на ее выполнение.
  • В этой книге термин "параметр" и "аргумент" используются по-разному. Первый для универсальных классов, второй - для подпрограмм. В традиционной программистской терминологии параметры и аргументы чаще всего синонимы.
Александр Шалухо
Александр Шалухо
Как сбросить прогресс по курсу? Хочу начать заново
Анатолий Садков
Анатолий Садков
Вопросик
Александр Качанов
Александр Качанов
Япония, Токио
Янош Орос
Янош Орос
Украина, Киев