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