ГЛАВА 3 ТИПЫ. При объявлении переменной необходимо указать ее тип. Тип переменной описывает набор значений, которые она может принимать, и действия, которые могут быть над ней выполнены. Объявление типа определяет идентификатор, который обозначает тип. ┌───────────┐ ┌───┐ ┌─────┐ ┌───┐ объявление ──Ў│ идентифи- ├──Ў│ = ├──Ў│ тип ├──Ў│ ; ├──Ў типа │ катор │ └───┘ └─────┘ └───┘ └───────────┘ ┌────────────────────┐ тип ────────┬────Ў│ простой тип │─────────Ў │ └────────────────────┘ ° │ ┌────────────────────┐ │ ├────Ў│ тип указателя ├────┤ │ └────────────────────┘ │ │ ┌────────────────────┐ │ ├────Ў│ структурный тип ├────┤ │ └────────────────────┘ │ │ ┌────────────────────┐ │ ├────Ў│ строковый тип ├────┤ │ └────────────────────┘ │ │ ┌────────────────────┐ │ └────Ў│ Идентификатор типа ├────┘ └────────────────────┘ Указание идентификатора в левой части объявления типа означает,что он определен как идентификатор типа для блока, в котором встречается это объявление типа. Область действия идентификатора типа не включает его самого, за исключением типа указатель. Имеется 6 больших классов типов: - простые типы; - строковые типы; - структурные типы; - тип указатель; - процедурные типы; - объектные типы. В последующих разделах будет описан каждый из этих классов. Простые типы Простые типы определяют упорядоченные множества значений. ┌────────────────────┐ простой тип ─────┬────Ў│ порядковый тип ├─────────Ў │ └────────────────────┘ ° │ ┌────────────────────┐ │ └────Ў│ вещественный тип ├─────┘ └────────────────────┘ ┌────────────────────┐ вещественный тип ─────Ў│ идентификатор ├─────Ў │ вещественного типа │ └────────────────────┘ Идентификатор вещественного типа относится к числу стандартных идентификаторов, которые могут быть Real, Single, Double, Extended или Comp. В главе 1 можно найти описание, как задавать константы целого и вещественного типов. Порядковые типы. Порядковые типы представляют собой подмножество простых типов. Все простые типы, отличные от вещественных типов, являются порядковыми и выделяются по следующим четырем характеристикам. - Все возможные значения данного порядкового типа представляют собой упорядоченное множество, и каждое возможное значение связано с порядковым номером, который представляет собой целочисленное значение. За исключением значений типа Integer, первое значение любого порядкового типа имеет порядковый номер 0, следующее значение имеет порядковый номер 1 и так далее для каждого значения в этом порядковом типе. Порядковым номером значения типа Integer является само это значение. В любом порядковом типе каждому значению, кроме первого, предшествует другое значение, и после каждого значения, кроме последнего, следует другое значение в соответствии с упорядоченностью типа. - К любому значению порядкового типа можно применить стандартную функцию Ord, возвращающую порядковый номер этого значения. - К любому значению порядкового типа можно применить стандартную функцию Pred, возвращающую значение, предшествующее этому значению. Если эта функция применяется к первому значению в этом порядковом типе, то выдается сообщение об ошибке. - К любому значению порядкового типа можно применить стандартную функцию Succ, возвращающую значение, следующее за этим. Если эта функция применяется к последнему значению в этом порядковом типе, то выдается сообщение об ошибке. Синтаксис порядкового типа имеет следующий вид: ┌────────────────────┐ порядковый ─────┬────Ў│ тип поддиапазона ├─────────Ў тип │ └────────────────────┘ ° │ ┌────────────────────┐ │ ├────Ў│ перечислимый тип ├─────┘ │ └────────────────────┘ │ │ ┌────────────────────┐ │ └────Ў│ идентификатор │ │ │ порядкового типа ├─────┘ └────────────────────┘ Turbo Pascal имеет семь встроенных порядковых типов: Integer (целый), Shortint (короткий целый), Longint (длинный целый), Byte (длиной в байт), Word (длиной в слово), Boolean (логический) и Char (символьный). Кроме того, имеется два других класса порядковых типов, определяемых пользователем: перечислимые типы и типы поддиапазона. Целочисленный тип В Turbo Pascal имеется пять встроенных целочисленных типов: Shortint (короткое целое), Integer (целое), Longint (длинное целое), Byte (длиной в байт) и Word (длиной в слово). Каждый тип обозначает определенное подмножество целых чисел, как это показано в следующей таблице. Таблица 3.1: Встроенные целочисленные типы ─────────────────────────────────────────────────────────────────── Тип Диапазон Формат ─────────────────────────────────────────────────────────────────── Shortint -128 .. 127 8 битов со знаком Integer -32768 .. 32767 16 битов со знаком Longint -2147483648 .. 2147483647 32 бита со знаком Byte 0 .. 255 8 битов без знака Word 0 .. 65535 16 битов без знака ─────────────────────────────────────────────────────────────────── Арифметические действия над операндами целого типа предполагают 8-, 16- и 32-битовую точность в соответствии со следующими правилами: - Тип целой константы представляет собой встроенный целочисленный тип с наименьшим диапазоном, включающим значение этой целой константы; - В случае бинарной операции, оба операнда преобразуются к их общему типу перед тем, как над ними производится операция. Общим типом является встроенный целочисленный тип с наименьшим диапазоном, включающим все возможные значения обоих типов. Например, общим типом для Integer и Byte является тип Integer, а общим типом для Integer и Word является LongInt. Действие выполняется в соответствии с точностью общего типа и типом результата является общий тип. - Выражение справа в операторе присваивания вычисляется независимо от размера или типа переменной в левой части. - Перед выполнением любой арифметической операции операнд размером в байт преобразуется в промежуточный операнд размером в слово, что совместимо как с типом Integer, так и с типом Word. Значение одного целочисленного типа может быть явным образом преобразовано к другому целочисленному типу с помощью приведения типов (Приведение типов описываются в главах 4 и 6). Логический тип Значения логического типа обозначаются встроенными идентификаторами констант False и True. Поскольку логический тип является перечислимым, между этими значениями имеют место следующие отношения: - False < True - Ord(False) = 0 - Ord(True) = 1 - Succ(False) = True - Pred(True) = False Символьный тип Множеством значений этого типа являются символы, упорядоченные в соответствии с расширенным набором символов кода ASCII (Приложение В). При вызове функции Ord(Ch), где Ch - значение типа Char, возвращается порядковый номер Ch. Строковая константа с длиной 1 может обозначать значение константы символьного типа. Любое значение символьного типа можно получить с помощью стандартной функции Chr. Перечислимые типы Перечислимые типы определяют упорядоченные наборы значений через перечисление идентификаторов, которые обозначают эти значения. Упорядочение наборов выполняется в соответствии с последовательностью, в которой перечисляются идентификаторы. ┌───┐ ┌───────────────┐ ┌───┐ перечислимый ──Ў│ ( ├───Ў│ список ├───Ў│ ) ├───Ў тип └───┘ │идентификаторов│ └───┘ └───────────────┘ список ┌─────────────┐ идентификаторов ────────Ў│идентификатор├───┬────Ў ° └─────────────┘ │ │ ┌───┐ │ └──────┤ , │ў───────────┘ └───┘ При указании идентификатора в списке идентификаторов перечислимого типа он объявляется как константа для блока, в котором объявляется перечислимый тип. Типом этой константы является объявленный перечислимый тип. Порядковый номер перечислимой константы определяется ее позицией в списке идентификаторов при объявлении. Перечислимый тип, в котором описывается константа, становится ее типом. Первая перечислимая константа в списке имеет порядковый номер 0. Приведем примеры перечислимого типа: type Suit = (Club, Diamond, Heart, Spade); Согласно этим объявлениям diamond является константой типа Suit. При применении функции Ord к значению перечислимого типа она возвращает целое число, которое показывает, какое положение занимает это значение по отношению к другим значениям этого перечислимого типа. Согласно предшествующим объявлениям, Ord(Club) возвращает 0, Ord(Diamond) возвращает 1 и так далее. Тип поддиапазона Тип поддиапазона представляет собой диапазон значений из порядкового типа, называемого главным типом. Определение типа поддиапазона включает наименьшее и наибольшее значения в поддиапазоне; оно имеет следующий синтаксис: тип ┌───────────┐ ┌────┐ ┌───────────┐ поддиапазона ───Ў│ константа ├───Ў│ .. ├───Ў│ константа ├───Ў └───────────┘ └────┘ └───────────┘ Обе константы должны быть одного порядкового типа. Типы поддиапазона, имеющие вид а .. b, требуют, чтобы а был меньше или равен b. Приведем примеры типов поддиапазона: 0 .. 99 -128 .. 127 Club .. Heart Переменная типа поддиапазона имеет все свойства переменных главного типа, однако ее значение на этапе выполнения должно принадлежать указанному интервалу. Из-за допущения константных выражений возникает синтаксическая неопределенность там, где стандартный Паскаль допускает только простые константы. Раcсмотрим следующие объявления: const X = 50; Y = 10; type Color = (Red, Green, Blue); Scale = (X - Y) * 2 .. (X + Y) * 2; Синтаксис стандартного Паскаля ставит непременным условием,что если определение типа началось с круглой скобки, то это перечислимый тип, подобно типу Color, определенному выше. Однако целью объявления Scale является задание типа поддиапазона. Решением этой проблемы является либо переделка первого выражения поддиапазона так, чтобы оно не начиналось с круглой скобки, или задание другой константы, равной значению этого выражения, и затем использование этой константы в определении типа: type Scale = 2 * (X - Y) .. (X + Y) * 2; Вещественные типы К вещественному типу относится подмножество вещественных чисел, которые могут быть представлены в формате с плавающей точкой с фиксированным числом цифр. Запись значения в формате с плавающей точкой обычно включает три значения - m, b, e - таким образом, что (m * b) в степени e = n, где b всегда равен 2, а m и e являются целочисленными значениями в диапазоне вещественного типа. Эти значения m и e далее определяют диапазон и точность вещественного типа. Имеются пять видов вещественных типов: Real, Single, Double, Extended и Comp. Вещественные типы различаются диапазоном и точностью связанных с ними значений (см. таблицу 3.2) Таблица 3.2 Вещественные типы данных ─────────────────────────────────────────────────────────────────── Тип Диапазон Значащие Размер в цифры байтах ─────────────────────────────────────────────────────────────────── Real 2.9x10**-39 .. 1.7x10**38 11-12 6 Single 1.5x10**-45 .. 3.4x10**38 7-8 4 Double 5.0x10**-324 .. 1.7x10**308 15-16 8 Extended 3.4x10**-4932 .. 1.1x10**4932 19-20 10 Comp -2**63 + 1 .. 2**63 - 1 19-20 8 ─────────────────────────────────────────────────────────────────── Примечание : Сложный тип содержит только целочисленные значения в диапазоне от -2**63 + 1 до 2**63 - 1, что приблизительно равно -9.2x10**18 и 9.2x10**18. Turbo Pascal поддерживает две модели генерации кода для выполнения действий над вещественными типами: программную для чисел с плавающей точкой и аппаратную для чисел с плавающей точкой. Выбор соответствующей модели осуществляется с помощью директивы компилятора $N. При отсутствии числового сопроцессора 8087 директива компилятора $E обеспечит полную эмуляцию числового сопроцессора 8087. Программная поддержка чисел с плавающей точкой. В состоянии {$N-} , которое устанавливается по умолчанию, генерируемый код выполняет все вычисления с вещественными типами программно, через вызов подпрограмм библиотеки времени выполнения. Из-за соображения скорости и размера кода в этом состоянии допускаются только действия над переменными типа Real (вещественный). Любая попытка оттранслировать операторы, выполняющие действия над типами Single, Double, Extended, Comp вызывает сообщение об ошибке. Аппаратная поддержка чисел с плавающей точкой. В состоянии {$N+} генерируемый код выполняет все вычисления над вещественными типами с помощью числового сопроцессора 8087. Это состояние позволяет использовать все пять вещественных типов. Turbo Pascal подключает библиотеку времени выполнения, которая автоматически эмулирует числовой сопроцессор 8087, если он отсутствует; для определения того будет или нет включен эмулятор в программу, используется директива компилятора $E. Более детальное описание генерации кода при аппаратной поддержке чисел с плавающей точкой и эмуляции числового сопроцессора 8087 вы можете найти в главе 14 "Использование 8087". Строковые типы Значением строкового типа является последовательность символов с динамическим атрибутом длины (в зависимости от действительного числа символов при выполнении программы) и константным атрибутом размера в диапазоне от 1 до 255. Строковый тип, объявленный без атрибута размера, имеет установленный по умолчанию атрибут размера, равный 255. Текущее значение атрибута длины можно получить с помощью стандартной функции Length. ┌──────┐ строковый тип ───Ў│string├──┬──────────────────────────────Ў └──────┘ │ ° │ ┌───┐ ┌─────┐ ┌───┐ │ └─Ў│ [ ├──Ў│целое├──Ў│ ] ├─┘ └───┘ │без │ └───┘ │знака│ └─────┘ Отношение между любыми двумя строковыми значениями устанавливается согласно отношению порядка между значениями символов в соответствующих позициях. В двух строках разной длины каждый символ более длинной строки без соответствующего символа в более короткой строке принимает значение "больше"; например, 'xs' больше, чем 'x'. Пустые строки могут быть равны только другим пустым строкам, и они являются строками с наименьшим значением. К символам в строке можно иметь доступ как к компонентам массива, что описывается в разделе "Массивы, строки и индексы" главы 4. Операторы работы со строковыми типами описываются в разделах "Строковые операторы" и "Операторы отношений" главы 6. Стандартные процедуры и функции для работы со строковыми типами описаны в разделе "Строковые процедуры и функции" главы 10. Структурные типы Структурный тип, характеризуемый методом структурирования и типами своих компонент, имеет более одного значения. Если тип компонент является структурным, то получаемый в результате структурный тип имеет более одного уровня структурирования. Структурный тип может иметь неограниченные уровни структурирования. ┌───────────────┐ структурный ──┬────────────────┬──Ў│ тип массив ├─────Ў тип │ ┌────────┐ ° │ └───────────────┘ ° └─Ў│ packed ├─┘ │ ┌───────────────┐ │ └────────┘ ├──Ў│ множественный ├──┤ │ │ тип │ │ │ └───────────────┘ │ │ ┌───────────────┐ │ ├──Ў│ файловый тип ├──┤ │ └───────────────┘ │ │ ┌───────────────┐ │ ├──Ў│ тип запись ├──┤ │ └───────────────┘ │ │ ┌───────────────┐ │ └──Ў│ объектный тип ├──┘ └───────────────┘ Слово packed в объявлении структурного типа требует от компилятора уплотнить хранимые данные, даже за счет уменьшения доступа к компоненте переменной этого типа. Слово packed не имеет никакого действия в Turbo Pascal, вместо этого упаковка выполняется здесь автоматически всюду, где это возможно. Примечание: Максимальный допустимый размер структурного типа в Turbo Pascal равен 65520 байт. Тип массив Массивы содержат фиксированное число компонент одного типа - тип компоненты. На приведенной ниже синтаксической диаграмме тип компоненты следует за словом of. ┌───────┐ ┌───┐ ┌───────┐ ┌───┐ ┌────┐ ┌─────┐ тип ──Ў│ array ├─Ў│ [ ├───Ў│ тип ├─┬─Ў│ ] ├─Ў│ of ├─Ў│ тип ├─Ў массив └───────┘ └───┘ ° │индекса│ │ └───┘ └────┘ └─────┘ │ └───────┘ │ │ ┌───┐ │ └────│ , │ў──┘ └───┘ тип ┌────────────────┐ индекса ───Ў│ порядковый тип ├───Ў └────────────────┘ В типах индекса, по одному для каждой размерности массива, указывается число элементов. Допустимыми типами индекса являются все порядковые типы, за исключением Longint и поддиапазонов Longint. Массив может быть проиндексирован по каждой размерности всеми значениями соответствующего типа индекса; число элементов поэтому равно числу значений в каждом типе индекса. Число размерностей является неограниченным. Приведем пример типа массив: array [1..100] of Real Если тип компоненты в типе массив также является массивом, то результат можно рассматривать как массив массивов или как один многомерный массив. Например, array [Boolean] of array [1..10] of array [Size] of Real интерпретируется компилятором точно так же, как array [Boolean, 1..10, Size] of Real. Кроме того, вы можете выразить packed array [1..10] of packed array [1..8] of Boolean как packed array [1..10, 1..8] of Boolean Для доступа к элементам массива необходимо указать идентификатор массива с одним или несколькими индексами в скобках (см. "Массив, строки и индексы" в главе 4). Тип массив, имеющий вид packed array [M..N] of Char где M меньше, чем N, называется упакованным строковым типом (слово packed можно опустить, поскольку оно не оказывает действия в Turbo Pascal). Упакованный строковый тип имеет некоторые свойства, не характерные для других типов массивов (см. "Тождественные и совместимые типы" далее в этой главе). Тип запись Тип запись cодержит установленное число компонент или полей, которые могут быть различных типов. Объявление типа запись указывает тип каждого поля и идентификатор, который именует поле. ┌────────┐ ┌─────┐ тип запись ───Ў│ record ├──┬────────────────Ў│ end ├──Ў └────────┘ │ ┌────────┐ ° └─────┘ └─Ў│ список ├─┘ │ полей │ └────────┘ список ┌────────────┐ полей ┬─Ў│ фиксирован-├─┬────────────────────────────┬──────────Ў │ │ ная часть │ │ ┌───┐ ┌────────────┐ ° │ ┌───┐ ° │ └────────────┘ └─Ў│ ; ├───Ў│ вариантная ├─┘ └─Ў│ ; ├─┘ │ └───┘ ° │ часть │ └───┘ └──────────────────────────┘ └────────────┘ ┌───────────┐ ┌───┐ ┌─────┐ фиксированная ────Ў│ список ├───Ў│ : ├───Ў│ тип ├──┬──Ў часть ° │ идентифи- │ └───┘ └─────┘ │ │ │ икаторов │ │ │ └───────────┘ │ │ ┌───┐ │ └────────────│ ; │ў───────────────────┘ └───┘ Фиксированная часть типа запись содержит список фиксированных полей вместе с идентификатором и типом для каждого поля. Каждое поле содержит информацию, которая всегда отыскивается одним и тем же способом. Приведем пример типа запись: type DateRec = record Year : Integer; Month : 1 .. 12; Day : 1 .. 31; end; В вариантной части, изображенной на синтаксической диаграмме объявления типа запись, память распределяется более чем для одного списка полей, поэтому доступ к информации может быть осуществлен более чем одним способом. Каждый список полей является вариантом. Варианты налагаются друг на друга в памяти, поэтому в любое время возможен доступ ко всем полям во всех вариантах. вариантная часть │ ┌────┐ ┌────────┐ ┌──┐ ┌───────┐ └─Ў│case├─┬───────────────────Ў│тип поля├─Ў│of├────Ў│вариант├─┬─Ў └────┘ │ ° │признака│ └──┘ ° └───────┘ │ │ ┌───────┐ ┌─┐ │ └────────┘ │ ┌─┐ │ └─Ў│иденти-├─Ў│:├─┘ └──────┤;│ў───┘ │фикатор│ └─┘ └─┘ └───────┘ ┌──────────────┐ тип поля ────Ў│идентификатор ├────Ў признака │порядкого типа│ └──────────────┘ ┌─────────┐ ┌─┐ ┌─┐ ┌─┐ вариант ────Ў│константа├─┬─Ў│;├──Ў│(├──┬──────────────Ў│)├───Ў ° └─────────┘ │ └─┘ └─┘ │ ° └─┘ │ ┌─┐ │ │ │ └────┤,│ў──────┘ │ ┌──────┐ │ └─┘ └─Ў│список├─┘ │полей │ └──────┘ Вы можете видеть на диаграмме, что каждый вариант идентифицируется по крайней мере одной константой. Все константы должны быть отличными друг от друга и иметь порядковый тип, совместимый с типом поля признака. Доступ к вариантным и фиксированным полям один и тот же. В вариантной части можно указать необязательный идентификатор - идентификатор признака поля. При наличии идентификатора признака поля он становится идентификатором дополнительного фиксированного поля записи - поля признака. Программа может использовать значение поля признака для указания, какой вариант является активным в настоящий момент. Без указания поля признака программа выбирает вариант по другому критерию. Ниже приводится несколько примеров типа запись: type Person = record FirstName, LastName: string[40]; BirthDate: Date; case Citizen: Boolean of True: (BirthPlace : string[40]); False: (Country: string[20]; EntryPort: string[20]; EntryDate: Date; ExitDate: Date); end; Polygon = record X, Y: Real; case Kind: Figure of Rectangle: (Height, Width: Real); Triangle: (Size1, Size2, Angle: Real); Circle: (Radius: Real); end; Типы объектов. Тип объект - это структура, состоящая из фиксированного числа компонент. Каждая компонента - это или поле, которое содержит данные определенного типа, или метод, который производит операции над объектом. Аналогично объявлению переменных, объявление поля, указывает тип данных поля и идентификатор имени этого поля; и аналогично объявлению процедуры или функции объявление метода указывает заголовок процедуры, функции, констрактора или дестрактора. Тип объект может наследовать компоненты от другого типа объекта. Если T2 наследует от Т1, то Т2 есть порожденный от Т1, а Т1 есть предок для Т2. Наследование является транзитивным, т.е. если Т3 наследует от Т2, а Т2 наследует от Т1, то Т3 так же наследует от Т1. Сфера действия типа объекта состоит из него самого и всех его порожденных типов. ┌──────┐ ┌────────────────┐ тип объекта ──Ў│object├─┬───────────────────Ў│список компонент├─┐ └──────┘ │ ┌────────────┐ ° └────────────────┘ │ └─Ў│Hаследование├─┘ │ └────────────┘ │ ┌───────────────────────────────────────────┘ │ ┌───┐ └─┬──────────────────────────────────┬──┤end├─Ў │ ┌───────┐ ┌────────────────┐ │ └───┘ └──Ў│private├──Ў│список компонент├─┘ └───────┘ └────────────────┘ ┌───┐ ┌──────────────────────────┐ ┌───┐ наследование ──Ў│ ( ├──Ў│идентификатор типа объекта├──Ў│ ) ├──Ў └───┘ └──────────────────────────┘ └───┘ список компонент ──┬─────────────────┬───────────────────Ў │ ┌────────┐ ° │ ┌─────────┐ ° └─Ў│ список ├──┘ └─Ў│ список ├──┘ │ полей │ │ методов │ └────────┘ └─────────┘ ┌──────────────────────┐ ┌───┐ ┌───┐ ┌───┐ список полей ────Ў│cписок идентификаторов├─Ў│ : ├─Ў│typ├─Ў│ ; ├─┬─Ў ° └──────────────────────┘ └───┘ └───┘ └───┘ │ └────────────────────────────────────────────────┘ ┌────────────────┐ ┌───┐ список методов ───Ў│заголовок метода├─┬───────────────────┤ ; ├─┬─Ў ° └────────────────┘ │ ┌───┐ ┌───────┐° └───┘ │ │ └Ў│ ; ├─Ў│virtual├┘ │ │ └───┘ └───────┘ │ └───────────────────────────────────────────────┘ ┌────────────────────────┐ заголовок метода ────┬───Ў│ заголовок процедуры ├──────Ў │ └────────────────────────┘ ° │ ┌────────────────────────┐ │ ├───Ў│ заголовок функции ├──┤ │ └────────────────────────┘ │ │ ┌────────────────────────┐ │ ├───Ў│ заголовок констрактора ├──┤ │ └────────────────────────┘ │ │ ┌────────────────────────┐ │ └───Ў│ заголовок дестрактора ├──┘ └────────────────────────┘ Следующий код показывает примеры объявления типов объектов. Эти объявления будут использоваться в других примерах этой главы. type Point = object X, Y: Integer; end; Rect = object A, B: Point; procedure Init(XA, YA, XB, YB: Integer); procedure Copy(var R: Rect); procedure Move(DX, DY: Integer); procedure Grow(DX, DY: Integer); procedure Intersect(var R: Rect); procedure Union(var R: Rect); function Contains(P: Point): Boolean; end; StringPtr = ^String; FieldPtr = ^Field; Field = object X, Y, Len: Integer; Name: StringPtr; constructor Copy(var F: Field); constructor Init(FX, FY, FLen: Integer; FName: String); destructor Done; virtual; procedure Display; virtual; procedure Edit; virtual; function GetStr: String; virtual; function PutStr(S: String): Boolean; virtual; end; StrFieldPtr = ^StrField; StrField = object(Field) Value: StringPtr; constructor Init(FX, FY, FLen: Integer; FName: String); destructor Done; virtual; function GetStr: String; virtual; function PutStr(S: String): Boolean; virtual; function Get: String; procedure Put(S: String); end; NumFieldPtr = ^NumField; NumField = object(Field) Value, Min, Max: Longint; constructor Init(FX, FY, FLen: Integer; FName: String; FMin, FMax : LongInt); function GetStr : String; virtual; function PutStr(S : String) : Boolean; virtual; function Get : Longint; procedure Put(S : Longint); end; ZipFieldPtr = ^ZipField; ZipField = object(NumField) function GetStr: String; virtual; function PutStr(S: String): Boolean; virtual; end; В отличие от других типов, тип объект может быть объявлен в части объявления типов в самой внешней части программы или модуля. Так, тип объекта не может быть объявлен в части объявления переменных или внутри процедуры, функции или метода. Компоненты и сфера действия. Компонента для типа файл не может быть объектного типа, а так же любой структурированный тип не может иметь компоненту объектного типа. Сфера действия идентификатора компоненты распространяется на сферу действия этого объектного типа. Более того, сфера действия идентификатора компоненты распространяется на процедуры, функции, констракторы и дестракторы которые реализуют методы этого объектного типа и его порожденных типов. По этой причине написание идентификатора компоненты должно быть уникальным внутри объектного типа, всех его порожденных типов и всех его методов. Сфера действия идентификатора компоненты, объявленной в разделе private (личный) объявления типа объекта, ограничено модулем или программой, которые содержат объявления типа объекта. Другими словами, идентификатор private компоненты действует как идентификатор обычной компоненты внутри модуля, содержащего объявление типа объекта, но вне этого модуля любой идентификатор private компоненты неизвестен и недоступен. Помещая связанные типы объектов в одном модуле, эти типы объектов могут иметь доступ к другим private компонентам, делая private компоненты неизвестными в других модулях. Методы. Объявление метода внутри типа объекта соответствует объявлению forward для этого метода. После объявления типа объекта и внутри сферы действия объявления этого объекта должна стоять реализация для этого метода. Когда требуется уникальная идентификация метода, используется уточненный идентификатор метода. Он состоит из идентификатора типа объекта, точки и последующего идентификатора метода. Подобно другим идентификаторам, перед уточненным идентификатором метода может стоять идентификатор модуля с точкой. Внутри описания типа объекта заголовок метода может указывать параметры, объявленные в объектном типе, даже если объявление еще не завершено. Это иллюстрируется методами Copy, Intersect и Union типа Rect в предыдущем примере. Виртуальные методы. По умолчанию методы являются статическими, но могут быть, за исключением констрактор-метода, виртуальными через включение директивы virtual в объявлении метода. Компилятор разрешает вызовы статического метода во время компиляции, в то время, как вызов виртуальных методов разрешается во время выполнения. Второй из них называется поздним связыванием. Если тип объекта объявляет или наследует любой виртуальный метод, то переменные этого типа должны быть инициализированы через вызов констрактора перед любым вызовом виртуального метода. Так любой тип объекта, который объявляет или наследует любой виртуальный метод, должен так же объявлять или наследовать по крайней мере один констрактор-метод. Объектный тип может перекрывать (переопределять) любые методы, наследуемые им от предка. Если объявление метода в порожденном типе указывает такой же идентификатор типа, как объявление в предке, то объявление в порожденном типе перекрывает объявление в предке. Сфера действия перекрытия распространяется на область порожденного типа, в котором он был определен и до тех пор, пока идентификатор метода не будет снова перекрыт. Перекрытие статического метода позволяет свободно изменять заголовок метода произвольным образом. Напротив, при перекрытии виртуального метода должен быть точно соблюден порядок, тип и имена параметров, а так же тип результата функции. Более того, перекрытие должно снова включать директиву virtual. Экземпляры объектов. Экземпляр объекта создается путем объявления переменных или типированной константы объектного типа или применением стандартной процедуры New с переменной типа указатель на объектный тип. Результирующий объект называется экземпляром объектного типа. var F: Field; Z: ZipField; FP: FieldPtr; ZP: ZipFieldPtr; В данных объявлениях F есть экземпляр Field, а Z - экземпляр ZipField. Также после применения New к FP и ZP, FP будет указывать на экземпляр Field, а ZP будет указывать на экземпляр ZipField. Если объектный тип содержит виртуальные методы, то экземпляры этого типа должны быть инициализированы посредством вызова констрактора перед любым вызовом виртуального метода. См. пример: var S: StrField; begin S.Init(1, 1, 25, 'FirstName'); S.Put('Frank'); S.Display; ... S.Done; end; Если S.Init не был вызван, то вызов S.Display в этом примере будет приводить к ошибке. Важно! Присваивание экземпляру объектного типа не означает инициализации экземпляра. Это правило инициализации так же применимо к экземплярам компонент структурированных типов. Например: var Comment: array[1..5] of StrField; I: Integer; begin for I := 1 to 5 do Comment[I].Init(1, I+10, 40, "Comment"); ... for I:= 1 to 5 do Comment[I].Down; end; Для динамических экземпляров инициализация обычно совмещена с распределением, а очистка обычно совмещена с освобождением, используя расширенный синтаксис New и Dispose. Пример: var SP: StrFieldPtr; begin New(SP, Init(1, 1, 25, "FirstName")); SP^.Put("Frank"); SP^.Display; ... Dispose(SP, Done); end; Указатель объектного типа совместим по присваиванию с указателем любого типа объекта предка, следовательно во время выполнения программы указатель типа объекта может указывать на экземпляр этого типа или экземпляр любого порожденного типа. Например, указатель типа ZipFieldPtr может быть присвоен указателям типа ZipFieldPtr, NumFieldPtr и FieldPtr во время выполнения программы, указатель на тип FieldPtr может быть или nil, или указатель на экземпляр Field, StrField, NumField, ZipField, или любой другой экземпляр, порожденный от Field. Это правило совместимости при присваивании указателей так же применимо к параметрам объектного типа. Например, методу Field.Copy может быть передан любой экземпляр Field, StrField, NumField, ZipField или любой другой экземпляр, порожденный от Field. Метод активизируется через квалификатор метода в форме Экземпляр.Метод, где Экземпляр - это экземпляр объектного типа, а Метод - это метод этого объектного типа. Для статических методов объявленный (во время компиляции) тип экземпляра определяет, какой метод должен быть активирован. Например, квалификаторы F.Init и FP^.Init будут всегда активизировать Field.Init, поскольку объявленный тип F и FP^ - Field. Для виртуальных методов выбирается актуальный (во время выполнения) тип экземпляра. Например, квалификатор FP^.Edit может активировать Field.Edit, StrField.Edit, NumField.Edit или ZipField. Edit в зависимости от актуального типа экземпляра, на который указывает FP. Вообще, нет способов определить, какой метод будет активирован квалификатором виртуального метода. Вы можете разработать программу (такую, как программу редактора входных форм), которая активирует FP^.Edit и позднее, без модификации этой программы, применить ее к экземпляру нового, непредвиденного порожденного типа от Field. Когда необходима гибкость такого рода, Вы должны применять объектный тип с открытым набором порожденных типов, что гораздо лучше ограниченного набора типа записи с вариантами. Тип множество. Диапазон значений типа множество представляет собой мощность множества для определенного порядкового типа (базового типа). Каждое возможное значение типа множество является подмножеством возможных значений базового типа. Переменная типа множество может принимать как все значения множества, так и ни одного. ┌─────┐ ┌────┐ ┌────────────────┐ тип множество ───Ў│ set ├───Ў│ of ├───Ў│ порядковый тип ├───Ў └─────┘ └────┘ └────────────────┘ Базовый тип не должен иметь более 256 возможных значений и порядковые значения верхней и нижней границы базового типа не должны превышать диапазона от 0 до 255. В силу этого базовый тип множества не может быть ShorInt, Integer, LongInt, Word. Операции над типами множеств описываются в разделе "Операции над множествами" в главе 6. В разделе "Констракторы множеств" в этой главе показано, как создать значение множества. Любой множественный тип может принимать значение [], которое называется пустым множеством. Файловые типы Файловый тип состоит из линейной последовательности компонент, которые могут иметь любой тип за исключением файлового типа или структурного типа, содержащего компоненту с файловым типом. Число компонент не устанавливается объявлением файлового типа. ┌──────┐ ┌────┐ ┌─────┐ файловый тип ───Ў│ file ├──┬─Ў│ of ├───Ў│ тип ├─────Ў └──────┘ │ └────┘ └─────┘ ° └─────────────────────┘ Если слово of и тип компоненты опущены, объявляется нетипированный файл. Нетипированные файлы - это низкоуровневые каналы В/В, используемые для доступа к любому дисковому файлу независимо от его внутреннего формата. Стандартный файловый тип Text определяет файл, содержащий символы, упорядоченные в строки. Текстовые файлы используют специальные процедуры ввода/вывода, которые описываются в главе 19 "Ввод и вывод". Тип указатель Тип указатель определяет множество значений, которые указывают на динамические переменные определенного типа, называемого базовым типом. Переменная типа Pointer содержит адрес динамической переменной в памяти. ┌───┐ ┌─────────────┐ тип указатель ───Ў│ ^ ├───Ў│ базовый тип ├──Ў └───┘ └─────────────┘ ┌─────────────────────┐ базовый тип ────Ў│ идентификатор типа ├───Ў └─────────────────────┘ Если базовый тип является еще не объявленным идентификатором, то он должен быть объявлен в той же самой части объявления, что и тип указатель. Переменной указателя можно присвоить значение с помощью процедуры New, оператора @ или функции Ptr. Процедура New отводит новую область памяти в куче для динамической переменной и сохраняет адрес этой области в переменной указателе. Оператор @ устанавливает переменную указателя на область памяти, содержащую существующую переменную, включая и те переменные, которые имеют идентификаторы. Функция Ptr устанавливает переменную указатель на определенный адрес в памяти. Зарезервированное слово nil обозначает константу со значением указателя, который ни на что не указывает. Встроенный тип Pointer обозначает нетипированный указатель, то есть указатель, который не указывает ни на какой определенный тип. Переменные типа Pointer не могут быть разименованы; указание символа ^ после такой переменной вызывает появление ошибки. Как и значение, обозначаемое словом nil, значения типа Pointer совместимы со всеми другими типами указателей. В разделе "Указатели и динамические переменные" в главе 4 можно найти синтаксис ссылки на динамические переменные, которые указываются с помощью переменной указателя. Процедурные типы. Стандартный Паскаль рассматривает процедуры и функции как часть программы, которую можно выполнить посредством вызова процедуры или функции. В Turbo Pascal более свободное отношение к процедурам и функциям: он позволяет рассматривать процедуры и функции как обьекты, которые можно присвоить переменным или которые могут выступать в качестве параметров. Такие действия стали возможными благодаря процедурным типам. Для более полной информации по процедурным типам обратитесь к разделу "Процедурные типы" в главе 8. Объявление процедурного типа задает параметры и, для функции, тип результата. процедурный тип │ │ ┌─────────┐ └┬Ў│procedure├─┬────────────────────────────────────────────────Ў │ └─────────┘ │ ┌────────────────────────────┐ ° ° │ └─Ў│список формальных параметров├─┘ │ │ └────────────────────────────┘ │ │ ┌────────┐ ┌─┐ ┌─────────┐│ └Ў│function├┬────────────────────────────────Ў│:├Ў│результат├┘ └────────┘│ ┌────────────────────────────┐° └─┘ └─────────┘ └Ў│список формальных параметров├┘ └────────────────────────────┘ Синтаксис объявления процедурного типа точно такой же, как определение заголовка процедуры или функции, за исключением того, что идентификатор после ключевого слова procedure или function опускается. Примеры объявлений процедурного типа: type Proc = procedure; SwapProc = procedure(var X, Y: Integer); StrProc = procedure(S: String); MathFunc = function(X: Real): Real: DeviceFunc = function(var F: Text): Integer; MaxFunc = function(A, B: Real; F: MathFunc): Real; Имена параметров в объявлении процедурного типа чисто декоративные - они не воздействуют на смысл объявления. Примечание: Turbo Pascal не позволяет Вам объявлять функции, возвращающие значения процедурного типа; результат функции должен быть String, Real, Integer, Char, Boolean, Pointer или перечисление. Тождественные и совместимые типы Два типа могут быть тождественными, и эта тождественность (идентичность) является обязательной в некоторых контекстах. В других случаях два типа должны быть только совместимы или совместимы для присваивания. Два типа являются тождественными, если они описаны вместе, или если их определение используют один и тот же идентификатор типа. Тождественность типов Тождественность типов требуется только для переменных фактических и формальных парамеров при вызове процедур и функций. Два типа, скажем Т1 и Т2, являются тождественными, если является истинным одно из следующих утверждений: Т1 и Т2 представляют собой один и тот же идентификатор типа; Т1 объявлен как эквивалентный типу, тождественному Т2. Второе условие означает, что Т1 не обязательно должен быть объявлен как непосредственно эквивалентный Т2. Следующее объявление типов: Т1 = Integer; Т2 = Т1; Т3 = Integer; Т4 = Т2; означают, что Т1, Т2, Т3, Т4 и Integer являются тождественными типами. Следующие объявления типов Т5 = set of Integer; Т6 = set of Integer; не определяют Т5 и Т6 как тождественные, поскольку set of Integer не является идентификатором типа. Две переменные, объявленные в одном и том же объявлении, например: V1, V2: set of Integer; имееют тождественные типы, поскольку их объявления не разделены. Объявления: V1: set of Integer; V2: set of Integer; V3: Integer; V4: Integer; означают, что V3 и V4 имеют тождественный тип, а V1 и V2 нет. Совместимость типов Иногда, например, в выражениях и операциях сравнения требуется совместимость типов. Совместимость типов, кроме того, является важной предпосылкой для совместимости при присваивании. Совместимость типов имеет место, если выполняется, по крайней мере, одно из следующих условий: - оба типа являются одинаковыми. - оба типа являются вещественными типами. - оба типа являются целочисленными. - один тип является поддиапазоном другого. - оба типа являются поддиапазонами одного и того же основного типа. - оба типа являются типами множеств с совместимыми базовыми типами. - оба типа являются упакованными строковыми типами с одинаковым числом компонент. - один тип является строковым, а другой тип является или строковым типом, или упакованным строковым типом, или типом Char. - один тип - Pointer, а другой является любым типом указателя - оба типа являются процедурными типами с тождественными типами результата, с тождественным числом параметров, с тождественными (один в один) типами параметров. Совместимость в операциях присваивания Совместимость по присваиванию необходима, если имеет место присваивание значения, например, в операторе присваивания или при передаче значений параметров. Значение типа Т2 является совместимым по присваиванию с типом Т1 (т.е. допустимо Т1:=Т2),если выполняется одно из следующих условий: - Т1 и Т2 имеют тождественные типы, и ни один из них не является файловым типом или структурным типом, содержащим компоненту с файловым типом на одном из своих уровней. - Т1 и Т2 являются совместимыми порядковыми типами и значения типа Т2 попадают в диапазон возможных значений Т1. - Т1 и Т2 являются вещественными типами и значения типа Т2 попадают в диапазон возможных значений Т1. - Т1 является вещественным типом, а Т2 является целочисленным типом. - Т1 и Т2 являются строковыми типами. - Т1 является строковым типом, а Т2 является типом Char. - Т1 является строковым типом, а Т2 является упакованным строковым типом. - Т1 и Т2 являются совместимыми упакованными строковыми типами. - Т1 и Т2 являются совместимыми типами множеств, и все значения типа Т2 попадают в диапазон возможных значений Т1. - Т1 и Т2 являются совместимыми типами указателей. - Т1 и Т2 являются совместимыми процедурными типами. - Т1 является типом процедурным типом, а Т2 является процедурой или функцией с тождественным типом результата, с идентичным числом параметров и тождественностью один-в-один между типами параметров. - Объект типа Т2 совместим по присваиванию с объ5ектом типа Т2, если Т2 находится в области определения Т1. - Указатель типа Р2, указывающий на тип объекта Т2, совместим по присваиванию с указателем типа Р1, указывающим на тип объекта Т1, если Т2 лежит в области определения Т1. На этапе компиляции и выполнения выдается сообщение об ошибке, если совместимость по присваиванию необходима, а ни одно из условий предыдущего списка не выполнено. Раздел объявления типов. Программы, процедуры и функции имеют для объявления типов специальную часть объявления типов. Например: type Range = Integer; Number = Integer; Color = (Red, Green, Blue); CharVal = Ord(`A') .. Ord(`Z'); TestIndex = 1..100; TestValue = -99..99; TestList = array [TestIndex] of TestValue; TestListPtr = ^TestList; Date = object Year: Integer; Month: 1..12; Day: 1..31; procedure SetDate(D, M, Y: Integer); function ShowDate: String; end; MeasureData = record When: Date; Count: TestIndex; Data: TestListPtr; end; MeasureList = array [1..50] of MeasureData; Name = string [80]; Sex = (Male, Female); Person = ^PersonData; PersonData = record Name, FirstName: Name; Age: Integer; Married: Boolean; Father, Child, Sibling: Person; case S: Sex of Male: (Bearded: Boolean); Female: (Pregnant: Boolean); end; PersonBuf = array [0..Size of (PersonData) -1] of Byte; People = file of PersonData; В этом примере Range, Number и Integer являются тождественными типами. TestIndex является совместимым и совместимым по присваиванию, но не тождественным, с типами Range, Number и Integer. Отметим использование константных выражений в объявлениях CharVal и PersonBuf.