Надклассовые типы (wide class types)
Все типы, производные и расширенные (прямо или косвенно) от определенного тэгового типа T, принадлежат одному иерархическому образованию класса, корнем которого будет тэговый тип T.
Тэговый тип T неявно ассоциирован с надклассовым типом который обозначает целый класс типов - иерархию включающую данный тэговый тип T
и все производные от него типы.
Надклассовый тип не имеет явного имени и обозначается как атрибут T'Class, где T - имя соответствующего тэгового типа.
В таком контексте, явно описанные типы удобно называть индивидуальными типами, чтобы подчеркнуть их отличие от надклассовых типов.
В иерархическом образовании класса каждый индивидуальный тип идентифицируется тэгом (tag), который скрыто присутствует в каждом тэговом типе (отсюда, собственно, и название "тэговые" типы).
Наличие такой информации позволяет осуществлять идентификацию индивидуального типа объекта во время выполнения программы (то есть, динамически) и, используя результат такой идентификации, находить для этого объекта соответствующую реализацию операций.
Следует также заметить, что такой подход является некоторым ослаблением традиционной модели строгой типизации Ады, поскольку любой тип, производный от типа T, может быть неявно преобразован в надклассовый тип T'Class.
Для демонстрации сказанного, предположим, что у нас имеется следующая иерархия типов:
Root | ----------------- | | Child_1 Child_2 | | | Grand_Child_2_1 |
Предположим также, что спецификация пакета, описывающего показанную выше иерархию типов, имеет следующий вид (заметим, что пустые записи и расширения использованы для упрощения примера):
package Simple_Objects is
-- тип Root и его примитивные операции type Root is tagged null record; function The_Name (Self: in Root) return String; procedure Show (Self: in Root'Class); -- тип Child_1 и его примитивные операции type Child_1 is new Root with null record; function The_Name (Self: in Child_1) return String; -- тип Child_2 и его примитивные операции type Child_2 is new Root with null record; function The_Name (Self: in Child_2) return String; -- тип Grand_Child_2_1 и его примитивные операции type Grand_Child_2_1 is new Child_2 with null record; function The_Name (Self: in Grand_Child_2_1) return String; end Simple_Objects; |
В этом случае, все типы показанной иерархии (Root, Child_1, Child_2 и Grand_Child_2_1) будут принадлежать к надклассовому типу Root'Class.
Надклассовые типы могут быть использованы при описании переменных, параметров подпрограмм и объектов ссылочного типа.
При описании любой переменной надклассового типа, следует учитывать, что любой надклассовый тип T'Class
является неограниченным, а это значит, что компилятору заранее не известен размер резервируемого для размещения такой переменной пространства.
Следовательно, при описании переменной надклассового типа, необходимо обязательно предусматривать инициализацию такой переменной начальным значением:
V : T'Class := значение_инициализации ; |
Следует заметить, что значение_инициализации, в свою очередь, может быть динамически вычисляемым выражением.
С учетом приведенной ранее иерархии типов, рассмотрим простой пример следующего описания переменной:
Instance : Root'Class := Child_1'(Root with null record); |
Формальный параметр подпрограммы также может иметь надклассовый тип.
В таком случае, каждый фактический параметр, принадлежащий к иерархическому образованию класса (или, короче, - к классу), будет совместим с формальным параметром.
Такие подпрограммы не будут ограничены использованием только одного специфического типа и могут принимать парараметр тип которого принадлежит указанному классу.
Как правило, такие подпрограммы называют надклассовыми подпрограммами. Например:
procedure Show (Self : in Root'Class); |
(напомним, что это все типы, производные от типа Root) будет совместим с формальным параметром Self. Например:
declare Root_Instance : Root; Child_1_Instance : Child_1; Child_2_Instance : Child_2; GRand_Child_2_1_Instance : GRand_Child_2_1; Instance : Root'Class := Child_1'(Root with null record); begin Show (Root_Instance); Show (Child_1_Instance); Show (Child_2_Instance); Show (GRand_Child_2_1_Instance); Show (Instance); end; |
type Root_Ref is access Root'Class; |
declare Any_Instance: Root_Ref; begin Any_Instance := new Child_1'(Root with null record); . . . Any_Instance := new Child_2'(Root with null record); . . . end; |
Таким образом, как показано в примере, индивидуальным типом объекта обозначаемого переменной Any_Instance сначала будет тип Child_1, а затем - Child_2.