unit ListDemo;
uses…,Figures, FigArc;
type
tpFigure=tpDot;
tpNode=^tNode;{для указателей разрешен опережающий вызов описания типа}
tNode= {хотя тип tNode определен только в этом месте}
record {каждый узел списка состоит из:}
pFigure:tpFigure; {-указателя на фигуру}
pPrevious:tpNode; {-указателя на предыдущий узел списка}
end;
tList=
object {объект этого типа будет управлять списком}
pLastNode:tpNode; {указатель на последний узел списка}
constructor Init; {инициализация полей и методов "управляющего"
объекта}
destructor Done;virtual;{уничтожение списка и высвобождение памяти}
procedure Add(pFig:tpFigure); {добавить узел со ссылкой на
pFig в конец списка}
procedure Report; {вывод на экран информации о списке}
end;
реализация методов tList
constructor tList. Init;
begin
pLastNode:=nil; {в списке узлов нет, и pLastNode никуда не указывает}
end;
procedure tList. Add(pFig:tpFigure); {в качестве параметра передается
указатель на фигуру, которую надо внести в список}
var pCurrent:tpNode; {локальная переменная типа 'указатель на узел'}
begin
New(pCurrent); {создали новый узел, он пока не в списке}
pCurrent^.pFigure:=pFig; {установили его указатель pFigure на фигуру,
на которую "настроен" pFig}
pCurrent^.pPrevious:=pLastNode; {установили его указатель pPrevious
на последний узел списка; если это первый узел списка,
то pPrevious получает значение nil - см. конструктор}
pLastNode:=pCurrent; {обозначили узел как последний в списке;
теперь наш узел состоит в списке}
end;
destructor tList. Done;
var pCurrent:tpNode;
begin
while pLastNode<>nil do {движемся от последнего узла списка до начала,
т. е. пока pLastNode не укажет на nil}
begin
pCurrent:=pLastNode; {настраиваем pCurrent на последний узел}
Dispose(pCurrent^.pFigure, Done);{высвобождаем память, занятую под
фигуру. Важно, что ее метод Done - виртуальный}
pLastNode:= pCurrent^.pPrevious; {обозначаем предыдущий узел как
последний}
Dispose(pCurrent); {устраняем узел, на который настроен указатель
pCurrent}
end;
end;
procedure tList. Report; {вывод на экран координат фигур списка}
var pCurrent:tpNode;
tmp:string;
begin
pCurrent:=pLastNode; {настроились на последний узел списка}
while pCurrent<>nil do {движемся по списку до первого узла}
begin
Str(pCurrent^.pFigure^.GetX:3,tmp); {в строку tmp вводим 3 цифры
значения X}
OutText('X='+tmp+' '); {выводим значение X на экран}
Str(pCurrent^.pFigure^.GetY:3,tmp); {в строку tmp – значениеY}
OutText('Y='+tmp+' '); {выводим значение Y на экран}
pCurrent:=pCurrent^.pPrevious; {настроились на предыдущий узел}
end;
end;
переменные основной программы
var
aList:tList; {экземпляр типа "список"}
pArc:tpArc; {указатель на дугу}
pCircle:tpCircle; {указатель на окружность}
основная программа
begin
InitGraph(GraphDriver, GraphMode,''); {инициализация графики}
if GraphResult<>GrOk then Halt(1); {в случае ошибки - остановка
программы}
aList. Init; {создали список. В нем нет узлов, а поле pLastNode равно nil}
aList. Add(New(tpArc, Init(120,80,20,10,90))); {добавили в список
дугу}
aList. Add(New(tpCircle, Init(300,200,100))); {добавили окружность}
aList. Add(New(tpCircle, Init(150,150,50))); {другую окружность}
aList. pLastNode^.pFigure^.Show; {показали последнюю фигуру списка}
aList. pLastNode^.pFigure^.MoveTo(200,200); {передвинули ее}
aList. Report; {вывели на экран координаты фигур списка}
aList. Add(New(tpDot, Init(100,100))); {добавили в список точку}
aList. pLastNode^.pPrevious^.pPrevious^.pFigure^.Show; {показали
третью с конца фигуру, т. е. первую окружность}
aList. pLastNode^.pFigure^.Move(20,50); {передвинули точку}
aList. Report; {вывели координаты фигур списка}
aList. Done; {уничтожили список}
OutTextXY('Press <Enter> to exit',0,10);
end.
Очередь, стек, двунаправленный список.
Совершенно аналогичным образом можно создавать объект — двунаправленный список, в котором каждый узел имеет указатели как "влево" (на предыдущий узел), так и "вправо" (на последующий). В таком объекте полезно иметь поля:
· pLastNode — указатель на последний узел списка,
· pFirstNode — указатель на первый элемент.
· nNodes — число имеющихся на данный момент узлов (в Delphi аналогичное поля обычно носит имя Count),
· pCurrent — указатель на текущий узел списка (с которым мы в данный момент работаем),
· nCurrent — номер текущего узла.
Кроме того, надо предусмотреть методы:
· Insert (i:integer, pFig:tpFigure) — вставка нового узла с фигурой, на которую настроен pFig, в позицию с номером i (при этом номер прежнего узла с номером i становится i+1 и т. д.),
· Delete (i:integer) — уничтожение узла i,
· Exchange(i, j) — обмен двух узлов местами.
Примеры использования нашего однонаправленного списка:
a. очередь ("первый вошел — первый вышел");
b. стек ("последний вошел — первый вышел").
В случае очереди мы создаем потомка класса 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:
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |


