Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Паскаль / tp3 / tp3 / 3

.doc
Скачиваний:
25
Добавлен:
10.12.2013
Размер:
140.8 Кб
Скачать

│ ┌───────────────────────────────┘ │ │

│ │ ┌─────────────────────────┐ │ │

│ └──│ целочисленная константа │─────┘ │

│ └─────────────────────────┘ │

└───────────────────────────────────────────────────────┘

┌─────────────────────┐

Заголовок метода ────┬─────────│ залоговок процедуры ├────────

│ └─────────────────────┘ ‑

│ ┌─────────────────────┐ │

├─────────│ заголовок функции ├──│

│ └─────────────────────┘ │

│ ┌────────────────────────┐ │

├───────│ заголовок конструктора ├─│

│ └────────────────────────┘ │

│ ┌────────────────────────┐ │

└──│ заголовок деструктора ├───────┘

└────────────────────────┘

Следующий исходный код приводит пример описания объектного типа. Далее во всем этом разделе вы найдете ссылки на данное описание.

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: integer;

constructor Init(FX, FY, FLen: integer; FName: string;

FMin, FMax: longint);

function GetStr: string; virtual;

function PutStr(S: string): boolean;

virtual;

function Get: longint;

function Put(N: longint);

end;

ZipFieldPtr = ^ZipField;

ZipField = object(NumField)

function GetStr: string; virtual;

function PutStr(S: string): boolean; virtual;

end;

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

Компоненты и область действия

Тип компоненты файлового типа не может иметь объектный тип или любой структурный тип, содержащий компоненты объектного типа.

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

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

Методы

Описание метода внутри объектного типа соответствует опережающему описанию метода (forward). Таким образом, где-нибудь после описания объектного типа, но внутри той же самой области действия, что и область действия объявления объектного типа, метод должен реализоваться путем определения его описания.

Если требуется уникальный идентификатор метода, то используется уточненный идентификатор метода. Он состоит из идентификатора типа объекта, за которым следуют точка и идентификатор метода. Как и любому другому идентификатору, идентификатору уточненного метода, если требуется, могут предшествовать идентификатор пакета и точка.

Внутри описания объектного типа в заголовке метода могут указываться параметры описываемого объектного типа, даже если описание еще не завершено. Это иллюстрируется методами Copy, Intersect и Union типа Rect предыдущего примера.

Виртуальные методы

По умолчанию, методы являются статическими, однако могут, за исключением конструкторов, быть виртуальными (посредством включения директивы virtual в описание метода). Компилятор разрешает ссылки на вызовы статических методов во время процесса компиляции, тогда как вызовы виртуальных методов разрешаются во время выполнения. Это иногда называют поздним связыванием.

Если объектный тип объявляет или наследует какой-либо виртуальный метод, то переменные этого типа должны быть инициализированы посредством вызова конструктора перед вызовом любого виртуального метода. Таким образом, объектный тип, который описывает или наследует виртуальный метод, должен также описывать или наследовать по крайней мере один метод-конструктор.

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

Переопределение статического метода не зависит от изменения заголовка метода. В противоположность этому, переопределение виртуального метода должно сохранять порядок, типы и имена параметров, а также типы результатов функций, если таковые имеются. Более того, переопределение опять же должно включать директиву virtual.

Динамические методы

В Турбо Паскале для Windows вводится дополнительный класс методов позднего связывания, которые называются динамическими методами. Динамические методы отличаются от виртуальных методов только тем, как они вызываются на этапе выполнения. Во всех других отношениях динамический метод можно рассматривать, как эквивалентный виртуальному методу.

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

procedure FileOpen(var Msg: TMessage); virtual 100;

Переопределение динамического метода должны точно соответствовать порядку, типу и именам параметров и типу результата функции. Переопределение должно также включать в себя директиву virtual, за которой следует тот же индекс динамического метода, что и заданный в объектном типе предка.

Примечание: Более подробно о динамических методах и различиях в вызове виртуальных и динамических методах рассказывается в Главе 17 "Объекты".

Создание экземпляров объектов

Экземпляр объекта создается посредством описание переменной или константы с типом объектного типа или путем применения стандартной процедуры 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, 'Первое имя');

S.Put ('Ah'yr');

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, 'первое_имя');

...

for I := 1 to 5 do

Comment [I].Done;

end;

Для динамических экземпляров инициализация, как правило, связана с размещением, а очистка - с удалением, что достигается благодаря расширенному синтаксису стандартных процедур New и Dispose. Например:

var

SP: StrFieldPtr;

begin

New (SP, Init (1, 1, 25, 'первое_имя');

SP^.Put ('Ah'yr');

SP^.Display;

...

Dispose (SP, Done);

end;

Указатель на объектный тип совместим по присваиванию с указателем на любой родительский тип, поэтому во время выполнения программы указатель на объектный тип может указывать на экземпляр этого типа или на экземпляр любого порожденного (производного) типа.

Например, указатель типа ZipFieldPtr может присваиваться указателям типа ZipFieldPtr, NumFieldPtr и FieldPtr, а во время выполнения программы указатель типа FieldPtr может либо иметь значение nil, либо указывать на экземпляр Field, 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 или ZipField.Edit, в зависимости от действительного типа экземпляра, на который указывает FP.

В общем случае, не существует никакого способа определить, какой метод был активизирован десигнатором виртуального метода. Вы можете разработать программу (аналогичную программе ввода бланков редактора), которая активизирует FP.Edit, а затем, без модификации этой программы, применить ее к экземпляру нового, непредвиденного дочернего по отношению к Field типа. Если желательна такого рода возможность расширения, то вам следует применять объектный тип с открытым для расширения множеством производный типов, а не тип записи с ограниченным множеством вариантов.

Множественные типы

Диапазон значений множественного типа представляет собой мощность множества для определенного порядкового типа (базового типа). Каждое возможное значение множественного типа является подмножеством возможных значений базового типа.

Переменная множественного типа может принимать как все значения множества, так и ни одного.

╒══════╕ ╒══╕ ┌──────────────┐

множественный тип──│ set ├─│of├──│порядковый тип├───

╘══════╛ ╘══╛ └──────────────┘

Базовый тип не должен иметь более 256 возможных значений, и порядковые значения верхней и нижней границы базового типа должны не превышать диапазона от 0 до 255. В силу этого базовый тип множества не может быть коротким целым (Shortint), целым (Integer), длинным целым (Longint) или словом (Word).

Примечание: Операции над множественными типами описываются в разделе "Операции над множествами" в Главе 6. В разделе "Конструкторы множеств" показано как определять значения множества.

Любой множественный тип может принимать значение [], которое называется пустым множеством.

Файловые типы

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

╒══════╕ ╒══╕ ┌─────┐

файловый тип ─────│ file ├┬──│of├───│ тип ├─────────────

╘══════╛│ ╘══╛ └─────┘ ‑

│ │

└────────────────────┘

Если слово of и тип компонента опущены, то тип обозначает нетипизованный файл. Нетипизованные файлы представляют собой описатели ввода-вывода нижнего уровня, в основном используемые для прямого доступа к любому файлу на диске, независимо от его внутреннего формата.

Стандартный файловый тип Text определяет файл, содержащий символы, упорядоченные в строки. Текстовые файлы используют специальные процедуры ввода-вывода, которые описываются в Главе "Ввод и вывод".

Типы указатель

Тип указатель определяет множество значений, которые указывают на динамические переменные определенного типа, называемого базовым типом. Переменная с типом указатель содержит адрес динамической переменной в памяти.

╒══╕ ┌───────────┐

тип указатель ────│ ^├───│базовый тип├──────────────────

╘══╛ └───────────┘

┌──────────────────┐

базовый тип ────────│идентификатор типа├──────────────────

└──────────────────┘

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

Переменной-указателю можно присвоить значение с помощью процедуры New, операции @ или функции Ptr. Процедура New отводит новую область памяти в динамически распределяемой области для динамических переменных и сохраняет адрес этой области в переменной-указателе. Операция @ ориентирует переменную-указатель на область памяти, содержащую существующую переменную, включая и те переменные, которые имеют идентификаторы. Функция Ptr устанавливает переменную-указатель на определенный адрес в памяти.

Зарезервированное слово nil обозначает константу со значением указателя, которая ни на что не указывает (пустой указатель).

Встроенный тип Pointer обозначает нетипизованный указатель, то есть указатель, который не указывает ни на какой определенный тип. Переменные типа Pointer могут быть разыменованы: указание символа ^ после такой переменной вызывает появление ошибки. Как и значение, обозначаемое словом nil, значения типа Pointer совместимы со всеми другими типами указателей.

Примечание: В разделе "Указатели и динамические переменные" в Главе 4 вы можете найти синтаксис ссылки на динамические переменые, которые указываются с помощью указателя-переменной.

Встроенный тип PChar обозначает указатель на строку, завершающуюся нулем. PChar описывается следующим образом:

type PChar = ^Char;

Турбо Паскаль для Windows поддерживает набор правил расширенного синтаксиса, облегчающих работу со строками с завершающим нулем с помощью типа PChar. Полную информацию по данному вопросу можно найти в Главе 13 "Модуль Strings".

Процедурные типы

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

Примечание: Полное описание процедурных типов можно найти в разделе "Процедурный типы".

В описании процедурного типа задаются параметры, а для функции - результат функции.

процедурный ╒═════════╕

тип ──┬───────│процедура├┬──────────────────────────────────────

│ ╘═════════╛│ ‑ ‑

│ │ ┌────────────────────────────┐ │ │

│ └─│список формальных параметров├───┘ │

│ └────────────────────────────┘ │

│ ╒═══════╕ ╒═╕ ┌─────────┐ │

└─│функция├─┬────────────────────────│:├──│результат├──┘

╘═══════╛ │ ‑ ╘═╛ └─────────┘

│ │

│ ┌─────────────────┐ │

└─│список формальных├──┘

│ параметров │

└─────────────────┘

По существу синтаксис записи процедурного типа в точности совпадает с записью заголовка процедуры или функции, только опускается идентификатор после ключевого слова 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;

Имена параметров в описании процедурного типа играют чисто декоративную роль - на смысл описание они не влияют.

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

Тождественные и совместимые типы

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

Тождественность типов

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

Два типа, скажем T1 и T2, являются тождественными, если является истинным одно из следующих утверждений: T1 и T2 представляю собой один и тот же идентификатор типа; T1 описан как эквивалентный типу, тождественному T2.

Второе условие означает, что T1 не обязательно должен быть описан как непосредственно эквивалентный T2. Следующие описания типов:

T1 = integer;

T2 = T1;

T3 = integer;

T4 = T2;

означают, что T1, T2, T3, T4 и integer являются тождественными типами. Следующие описания типов:

T5 = set of integer;

T6 = set of integer;

не определяют T5 и T6 как тождественные, поскольку set of integer не является идентификатором типа. Две переменные, описанные в одном и том же описании, например:

V1, V2: set of integer;

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

V1: set of integer;

V2: set of integer;

V3: integer;

V4: integer;

означают, что V3 и V4 имеют тождественный тип, а V1 и V2 - нет.

Совместимость типов

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

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

- Оба типа являются тождественными.

- Оба типа являются вещественными типами.

- Оба типа являются целочисленными.

- Один тип является поддиапазоном другого.

- Оба типа являются отрезками одного и того же основного типа.

- Оба типа являются множественными типами с совместимыми базовыми типами.

- Оба типа являются упакованными строковыми типами с одинаковым числом компонентов.

- Один тип является строковым, а другой тип является или строковым типом, или упакованным строковым типом, или символьным типом.

- Один тип является указателем (Pointer), а другой является любым типом указателей.

- Один из типов представляет собой тип PChar, в другой - символьный массив с нулевой базой вида array[0..X] of Char (это применимо только при разрешении с помощью директивы {$X+} расширенного синтаксиса).

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

Совместимость в операциях присваивания

Совместимость по присваиванию необходима, если имеет место присваивание значения, например, в операторе присваивания или при передаче значений параметров.

Значение типа T1 является совместимым по присваиванию с типом T2 (то есть допустим оператор T1:=T2), если выполняется одно из следующих условий:

- T1 и T2 имеют тождественные типы, и ни один из них не является файловым типом или структурным типом, содержащим компонент с файловым типом на одном из своих уровней.

- T1 и T2 являются совместимыми перечислимыми типами, и значения типа T2 попадают в диапазон возможных значений T1.

- T1 и T2 являются вещественными типами, и значения типа T2 попадают в диапазон возможных значений T1.

- T1 является вещественным типом, а T2 является целочисленным типом.

- T1 и T2 являются строковыми типами.

- T1 является строковым типом, а T2 является символьным типом.

- T1 является строковым типом, а T2 является упакованным строковым типом.

- T1 и T2 являются совместимыми упакованными строковыми типами.

- T1 и T2 являются совместимыми множественными типами, и все члены значения типа T2 попадают в диапазон возможных значений T1.

- T1 и T2 являются совместимыми типами указателей.

- T1 представляет собой PChar, а T2 - строковую константу (это применимо только при разрешении с помощью директивы {$X+} расширенного синтаксиса).

- T1 представляет собой тип PChar, а T2 - символьный массив с нулевой базой вида array[0..X] of Char (это применимо только при разрешении с помощью директивы {$X+} расширенного синтаксиса).

- T1 и T2 являются совместимыми процедурными типами.

- T1 представляет собой процедурный тип, а T2 - процедура или функция с идентичным типом результата, идентичным числом параметров и соответствием между типами параметров.

- Объектны тип T2 совместим по присваиванию с объектным типом T1, если T2 является доменом T1.

- Тип указателя Р2, указывающий на объект типа Т2, совместим по присваиванию с типом указателя P1, указывающим на объект T1, если T2 является доменом T1.

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

Раздел описания типов

Программы, процедуры и функции имеют для описания типов специальный раздел описания типов. Например:

type

Range = integer;

Number = integer;

Color = (red,green,blue);

CharVal = Ord('A')..Ord('Z');

TextIndex = 1..100;

TestValue = -99..99;

TestList = array[TestIndex] of TestValue;

TestListPtr = ^TestList;

Date = record

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..SizeOf(PersonData)-1] of byte;

People = file of PersonData;

IntFile = file of integer

В этом примере Range, Number и integer являются тождественными типами. TestIndex является просто совместимым и совместимым по присваиванию, но не тождественным с типами Number, Range и integer. Обратите внимание на использование в описаниях CharVal и PersonBuf выражений-констант.

Соседние файлы в папке tp3