Примеры использования нашего однонаправленного списка:
очередь ("первый вошел — первый вышел"); стек ("последний вошел — первый вышел").В случае очереди мы создаем потомка класса tList:
tQueue=object(tList)
procedure Remove;
end;
в котором процедура Remove удаляет первый узел списка, при этом оставшаяся часть списка не меняется, но второй узел становится первым и т. д., т. е. весь список как бы сдвигается на один узел к началу. В случае стека мы создаем потомка класса tList, описание которого совпадает с предыдущим случаем:
tSteck=object(tList)
procedure Remove;
end;
но в котором процедура Remove удаляет не первый, а последний узел. При этом как и в предыдущем случае оставшаяся часть списка не меняется.
ОСОБЕННОСТИ ЯЗЫКА OBJECT PASCAL (среда Delphi, версия 2.0 и последующие).
Классы, объекты, поля и методы.
В Object Pascal класс — тип записи, который может иметь в своем составе поля данных и методы (а также свойства, но о них будет отдельный разговор).
Пример описания класса и переменной соответствующего типа:
type
tMyObject=
class(tObject)
fMyField1:Integer;
procedure MyMethod1;
function MyMethod2(X:real):real;
end;
var aMyObject:tMyObject;
Переменная такого типа (в нашем случае aMyObject) называется экземпляром класса, или объектом. Поля и методы значат то же, что и в Turbo Pascal. Перед именем полей обычно ставят букву f (сокращение от field — "поле").
Тут и в дальнейшем имена типа MyObject, fMyField, MyMethod и т. п. используются для того, чтобы явно подчеркнуть произвольность соответствующего имени и не путать такие имена с зарезервированными словами или предопределенными типами.
Свойства.
В Delphi пожелание ООП "не обращаться из программы к полям, а действовать через методы" нашло отражение в новом для ООП принципе — свойствах объекта.
Свойство определяется:
полем, некого типа, методом по чтению этого поля (функцией) того же типа, методом по записи в поле (процедурой) с параметром того же типа.type
tMyObject=
class(tObject)
fMyField:tMyType;
function getMyField:tMyType;
procedure setMyField (NewValue:tMyType);
property MyProperty:tMyType
read getMyField
write setMyField;
end;
Имена методов, конечно, задаются пользователем произвольно, и вместо этих имен могли бы, к примеру, использоваться Method1 и Method2. Тела методов getMyField и setMyField как и для всех других методов, должны быть описаны в разделе реализации. При этом у функции getMyField не должно быть параметров, а у setMyField – один параметр, через который передается (по имени или по значению) устанавливаемый параметр. Методы по чтению и записи не нужно явно вызывать в программе. Если имеются переменные, описанные как
var aMyObject:tMyObject;
aValue, aVariable:tMyType;,
то можно писать
aMyObject. MyProperty:=aValue;
aVariable:=aMyObject. MyProperty;
То есть внешне свойства выглядят как поля данных, но при чтении или записи значений в такие поля реально вызываются методы для их чтения или записи. Например, можно осуществлять проверки правильности ввода значений, а также некие другие действия (если свойство — координата фигуры, можно при присваивании координате нового значения вызвать перемещение фигуры по экрану). Если свойство должно только читаться или только записываться, может присутствовать лишь соответствующий метод (вызов другого приведет к диагностике ошибки при компиляции):
type
tAnObject=
class(tObject)
fMyField:tSomeType;
function GetValue:tSomeType;
property MyProperty:tSomeType
read GetValue;
end;
Если метод по чтению или записи просто должен читать или записывать в некое поле, вместо имени соответствующего метода (или обоих) можно непосредственно писать имя поля:
tSomeType
read myField
write myField;
Заметим, что свойство и его методы по чтению и записи, а также соответствующее поле данных обычно располагают в разных областях видимости: свойство делают published или public, а методы и поле данных – private или protected (см. далее).
Свойству можно присвоить значение по умолчанию с помощью ключевого слова default:
property active:Boolean
read GetActive
write SetActive {тут нет ’;’ !!!}
default true;
Свойство может выглядеть как массив (т. е. быть векторным):
property MyArray[I:Integer]:tMyType
read GetMyValue
write SetMyValue;
При этом функция GetMyValue должна быть совместима по типу с tMyType и иметь единственный параметр с тем же типом и именем, что и индекс свойства (обычно — целый тип):
function GetMyValue(I:Integer):tMyType;
Метод для записи должен первым параметром иметь индекс, а вторым — переменную нужного типа (ее можно передавать как по имени, т. е. с var, так и по значению):
procedure SetMyValue(I:Integer;NewValue:tMyType);
Можно определить некое главное, основное векторное свойство класса как default, и не упоминать его при доступе к этому свойству объекта по индексу. Не путать со значением default для свойства!
Пример: свойство Strings, являющееся массивом строк, определенное как default свойство для класса tMyObject:
type
tMyObject=
class(tObject)
...
property Strings[I:Integer]:string
read GetS
write PutS; {!!!после PutS обязательно ";"}
default;
...
end;
var aMyObject:tMyObject;
begin
...
aMyObject. Strings[1]:='обычный способ';
...
aMyObject[2]:='сокращенная форма';
...
end.
Векторные свойства могут быть многомерными. При этом порядок следования индексных параметров и их сигнатура должны соответствовать индексам массива, а в методе по записи после индексных параметров должен стоять параметр (такой же размерности и того же типа, что и свойство) для передачи свойству значения. Этот параметр может передаваться по имени или по значению. Как правило, для векторных свойств используется передача по имени, т. к. при передаче по значению массив должен быть продублирован, что занимает много оперативной памяти и времени.
В Delphi предусмотрен еще один способ передачи параметров в процедуру: передача константы по имени (пишется директива const вместо var перед именем переменной). Все происходит так же, как при директиве var, но при этом внутри процедуры не может быть изменено значение этой переменной (как при передаче по значению). На этапе компиляции отслеживаются ошибочные попытки изменить значения переменной путем присвоения или передачи параметра по имени в какую-либо процедуру.
Пример двумерного свойства:
property MyMatrix[Row, Col:Integer]:real
read GetElement
write SetElement;
Описание методов в этом случае может быть таким:
function GetElement(Row, Col:Integer):real;
procedure SetElement(Row, Col:Integer; const NewValue:Real);
Если многомерное свойство определено как default для данного класса, то к нему можно обращаться по индексу через имя объекта аналогично случаю одномерного векторного свойства.
4.10. Создание и уничтожение объектов. Конструкторы и деструкторы.
В Object Pascal объекты могут быть только динамическими! Любая переменная объектного типа — это указатель, но доступ к объекту идет без 'шляпки', а создание нового объекта осуществляется не оператором New, а с помощью вызова конструктора — функции, возвращающей указатель на создаваемый объект. Соответственно, уничтожение объекта производится не с помощью Dispose, а вызовом деструктора. Поэтому обычно в Delphi конструктор называют Create ("создать"), а деструктор — Destroy ("уничтожить, разрушить"):
aMyObject:=tMyObject. Create; {создали объект aMyObject типа tMyObject
и запомнили указатель на него}
...
aMyObject. Destroy; {уничтожили объект}
Заметим, что конструктор — это метод класса, и он работает без экземпляра объекта (экземпляра еще нет, но метод создает его). В Delphi конструкторы могут быть виртуальными, и их может быть несколько. Сразу при входе в конструктор желательно проинициализировать прародителя:
constructor tMyObject. Create;
begin
inherited Create;
...
end;
В данном примере инициализация прародителя осуществляется с помощью специального ключевого слова inherited, которое появилось еще в TurboPascal 7.0. Оно означает, что следующий за ним вызов относится к непосредственному прародителю данного объекта. Многократные вызовы типа inherited inherited запрещены, чтобы не вызвать неправильной инициализации объекта.
Если наш класс описан как
tMyObject=class(tObj1)
...
end;
то вызов inherited Create абсолютно логичен вызову tObj1.Create.
4.11. Наследование. Статические, виртуальные, динамические и абстрактные методы.
Все классы Delphi — потомки класса tObject. Если нет указания, какой класс является прародителем, значит, это tObject. Определение
tMyObject=class
...
end;
полностью равносильно
tMyObject=class(tObject)
...
end;
Вызываемые методы делятся на статические (static), виртуальные (virtual) и динамические (dynamic). Есть еще методы, которые не содержат никаких действий (нет реализации), никогда не вызываются и нужны только для наследования — абстрактные (abstract). Абстрактными могут быть только виртуальные или динамические методы, причем у них имеется только заголовок (в описании класса) и отсутствует реализация.
Переопределение методов в потомках называется перекрытием этих методов, а сами переопределенные методы прародителя — перекрытыми (override) потомком.
Перекрытие статических методов происходит так же, как в Turbo Pascal. Для виртуальных методов синтаксис изменен: в первом из прародителей, где определяется виртуальный метод, ставится ключевое слово virtual, а в потомках для перекрывающих методов — override.
Для виртуальных методов в классе постоянно хранятся адреса всех имеющихся в данном классе виртуальных методов независимо от того, унаследованы они или перекрыты. Виртуальные методы вызываются быстро, но таблица виртуальных методов (VMT) может занимать много места, если имеется длинная цепочка наследования с большим количеством методов в каждом классе. Динамические методы являются более экономичными по памяти и более медленным аналогам виртуальных. Каждому динамическому методу системой присваивается уникальный индекс. В таблице динамических методов (DMT) класса хранятся индексы и адреса только тех динамических методов, которые описаны в данном классе. Если при вызове нужный метод в таблице не найден, просматривается таблица прародителя, и т. д. вплоть до tObject. В остальном разницы между виртуальными и динамическими методами нет. Стоит отметить, что Delphi предоставляет существенно больший набор средств программирования, чем, например, Java, где ни статических, ни виртуальных методов нет, а только динамические.
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |


