3. Создание экземпляров объектов
Экземпляр объекта создается посредством описания переменной или константы объектного типа или путем применения стандартной процедуры New к переменной типа «указатель на объектный тип». Результирующий объект называется экземпляром объектного типа;
var
F: TField;
Z: TZipField;
FP: PField;
ZP: PZipField;
С учетом этих описаний переменных, F является экземпляром TField, a Z – экземпляром TZipField. Аналогично, после применения New к FP и ZP FP будет указывать на экземпляр TField, a ZP – на экземпляр TZipField.
Если объектный тип содержит виртуальные методы, то экземпляры этого объектного типа должны инициализироваться посредством вызова конструктора перед вызовом любого виртуального метода.
Ниже приведен пример:
var
S: StrField;
egin
S.Init (1, 1, 25, 'Первое имя');
S.Put ('Владимир');
S.Display;
...
S.Done;
end.
Если S.Init не вызывался, то вызов S.Display приведет к неудачному завершению данного примера.
Присваивание экземпляра объектного типа не подразумевает инициализации экземпляра. Объект инициализируется кодом, генерируемым компилятором, который выполняется между вызовом конструктора и моментом когда выполнение фактически достигает первого оператора в блоке кода конструктора.
Если экземпляр объекта не инициализируется и проверка диапазона включена (директивой {SR+}), то первый вызов виртуального метода экземпляра объекта дает ошибку этапа выполнения. Если проверка диапазона выключена (директивой {SR—}), то первый вызов виртуального метода неинициализированного объекта может привести к непредсказуемому поведению.
Правило обязательной инициализации применимо также к экземплярам, которые являются компонентами структурных типов. Например:
var
Comment: array [1..5] of TStrField;
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 ('Владимир');
SP^.Display;
.
.
.
Dispose (SP, Done);
end.
Указатель на объектный тип является совместимым по присваиванию с указателем на любой родительский объектный тип, поэтому во время выполнения программы указатель на объектный тип может указывать на экземпляр этого типа или на экземпляр любого дочернего типа.
Например, указатель типа ZipFieldPtr может присваиваться указателям типа PZipField, PNumField и PField, а во время выполнения программы указатель типа PField может либо иметь значение nil, либо указывать на экземпляр TField, TNumField, или TZipField, или на любой экземпляр дочернего по отношению к TField типа.
Эти правила совместимости указателей по присваиванию применимы также к параметрам – переменным объектного типа. Например, методу TField.Сору могут быть переданы экземпляры типов TField, TStrField, TNumField, TZipField или любые другие экземпляры дочернего от TField типа.
4. Компоненты и область действия
Область действия идентификатора компоненты простирается за пределы объектного типа. Более того, область действия идентификатора компонента простирается сквозь блоки процедур, функций, конструкторов и деструкторов, которые реализуют методы объектного типа и его наследников. Исходя из этих соображений написание идентификатора компоненты должно быть уникальным внутри объектного типа и внутри всех его наследников, а также внутри всех его методов.
Область действия идентификатора компонента, описанного в части private описания типа, ограничивается модулем (программой), которая содержит описание объектного типа. Другими словами, частные (private) компоненты-идентификаторы действуют как обычные общедоступные идентификаторы в рамках модуля, который содержит описание объектного типа, а вне модуля любые частные компоненты и идентификаторы неизвестны и недоступны. Поместив в один модуль связанные типы объектов, можно сделать так, что эти объекты смогут обращаться к частным компонентам друг друга, и эти частные компоненты будут неизвестны другим модулям.
В описании объектного типа заголовок метода может задавать параметры описываемого объектного типа, даже если описание еще неполное.
Описание метода внутри объектного типа соответствует опережающему описанию метода (forward). Таким образом, где-нибудь после описания объектного типа, но внутри той же самой области действия, что и область действия описания объектного типа, метод должен реализоваться путем определения его описания.
Для процедурных и функциональных методов определяющее описание имеет форму обычного описания процедуры или функции с тем исключением, что в этом случае идентификатор процедуры или функции рассматривается как идентификатор метода.
Для методов конструкторов и деструкторов определяющее описание принимает форму описания процедурного метода с тем исключением, что зарезервированное слово procedure заменяется зарезервированным словом constructor или destructor.
Определяющее описание метода может повторять (но не обязательно) список формальных параметров заголовка метода в объектном типе. В этом случае заголовок метода должен в точности повторять заголовок в объектном типе в порядке, типах и именах параметров и в типе возвращаемого функцией результата, если метод является функцией.
В определяющем описании метода всегда присутствует неявный параметр с идентификатором Self, соответствующий формальному параметру-переменной, обладающему объектным типом. Внутри блока метода Self представляет экземпляр, компонент метода которого был указан для активизации метода. Таким образом, любые изменения значений полей Self отражаются на экземпляре.
Область действия идентификатора компонента объектного типа распространяется на блоки процедур, функций, конструкторов и деструктора, которые реализуют методы данного объектного типа. Эффект получается тот же, как если бы в начало блока метода был вставлен оператор with в следующей форме:
with Self do
begin
...
end;
Исходя из этих соображений написание идентификаторов компонентов, формальных параметров метода, Self и любого идентификатора, введенного в исполняемую часть метода, должно быть уникальным.
Если требуется уникальный идентификатор метода, то используется уточненный идентификатор метода. Он состоит из идентификатора типа объекта, за которым следуют точка и идентификатор метода. Как и любому другому идентификатору, идентификатору уточненного метода, если требуется, могут предшествовать идентификатор пакета и точка.
Виртуальные методыПо умолчанию методы являются статическими, однако они могут, за исключением конструкторов, быть виртуальными (посредством включения директивы virtual в описание метода). Компилятор разрешает ссылки на вызовы статических методов во время процесса компиляции, тогда как вызовы виртуальных методов разрешаются во время выполнения. Это иногда называют поздним связыванием.
Если объектный тип объявляет или наследует какой-либо виртуальный метод, то переменные этого типа должны быть инициализированы посредством вызова конструктора перед вызовом любого виртуального метода. Таким образом, объектный тип, который описывает или наследует виртуальный метод, должен также описывать или наследовать, по крайней мере, один метод-конструктор.
Объектный тип может переопределять любой из методов, которые он наследует от своих родителей. Если описание метода в потомке указывает тот же идентификатор метода, что и описание метода в родителе, то описание в потомке переопределяет описание в родителе. Область действия переопределяющего метода расширяется до сферы действия потомка, в котором этот метод был введен, и будет оставаться таковой, пока идентификатор метода не будет переопределен снова.
Переопределение статического метода не зависит от изменения заголовка метода. В противоположность этому, переопределение виртуального метода должно сохранять порядок, типы и имена параметров, а также типы результатов функций, если таковые имеются. Более того, переопределение опять же должно включать директиву virtual.
Динамические методыBorland Pascal поддерживает дополнительные методы с поздним связыванием, которые называются динамическими методами. Динамические методы отличаются от виртуальных только характером их диспетчеризации на этапе выполнения. Во всех других отношениях динамические методы считаются эквивалентными виртуальным.
Описание динамического метода эквивалентно описанию виртуального метода, но описание динамического метода должно включать в себя индекс динамического метода, который указывается непосредственно за ключевым словом virtual. Индекс динамического метода должен быть целочисленной константой в диапазоне от 1 до 656535 и должен быть уникальным среди индексов других динамических методов, содержащихся в объектном типе или его предках. Например: