Ада-95. Компилятор GNAT
66ac8edd

Надклассовые типы (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);

Здесь, Instance - это переменная надклассового типа Root'Class, а ее индивидуальный тип указывается значением инициализации как тип Child_1.

Формальный параметр подпрограммы также может иметь надклассовый тип.

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

Такие подпрограммы не будут ограничены использованием только одного специфического типа и могут принимать парараметр тип которого принадлежит указанному классу.

Как правило, такие подпрограммы называют надклассовыми подпрограммами. Например:

procedure Show (Self : in Root'Class);

В результате, любой фактический параметр, который принадлежит надклассовому типу Root'Class тэгового типа Root



(напомним, что это все типы, производные от типа 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 имеет тип надклассовой ссылки Root_Ref и может обозначать любой объект который принадлежит классу Root'Class.

Таким образом, как показано в примере, индивидуальным типом объекта обозначаемого переменной Any_Instance сначала будет тип Child_1, а затем - Child_2.


Содержание раздела