Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

ИмяПеременнойОбъекта.ИмяКомпонента




причем так осуществляется прямой доступ и к полям, и к методам, и к свойствам, если конечно они не инкапсулированы.

ПРИМЕР. Вариант модуля для работы с однонаправленными линейными списками, использующий понятие класс, и пример использования этого модуля – построение ряда Фаррея.

В нижеприведенном варианте модуля удалось устранить недостатки, отмеченные для вариантов, ранее рассмотренных в теме «Модули».

· Теперь модуль L1List позволяет работать одновременно с несколькими экземплярами типа «Линейный однонаправленный список». Можно, например, описать 3 списка, создать их, и независимо их обрабатывать:

VAR Sp1,Sp2,Sp3:L1List.CL1List; … Sp1:=L1List.CL1List.Create; … Sp2:=L1List.CL1List.Create; … Sp3:=L1List.CL1List.Create;

… Sp1. InsPred … Sp2. InsPred … Sp3. InsPred …

… Sp1.GetNext; … Sp2.GetNext; … Sp3.GetNext; …

· При этом не возникает необходимости, ни в тиражировании модуля, ни в его параметризации – механизм тиражирования и параметризации встроен в семантику (и синтаксис) понятий класс-объект.

· Также достигнут приемлемый уровень защищенности модуля. Правда, «торчат уши» – определения типов TPElem и TElem, но они не открывают прямого доступа к приватным полям PFirst,PCur,PPred и не открывают возможностей внешнего вмешательства в функционирование инструментов модуля.(*)

PROGRAM\OOP\FARREJ.003\FARREJ.DPR

{Файл UGlob.PAS}

UNIT UGlob; { Это модуль "общих понятий" - для межмодульных связей, }

{ его можно сменить и основной модуль L1List, }

{ не требуя каких либо изменений, будет работать }

{ с однонаправленными линейными списками, хранящими не }

{ "числители и знаменатели", а другую информацию. }

INTERFACE

TYPE TVal= RECORD Ch,Zn:INTEGER END;

PROCEDURE WriteTVal(VAR PrmTxtF:TextFile;PrmVal:TVal);

IMPLEMENTATION

PROCEDURE WriteTVal; BEGIN WRITE(PrmTxtF,PrmVal.Ch,':',PrmVal.Zn)

END;

END.


{Файл L1List.PAS}

UNIT L1List; { Это основной модуль примера - вариант инструментария}

{ для работы с однонаправленными линейными списками. }

INTERFACE

USES UGlob;

TYPE TPElem=^TElem; TElem= RECORD Inf:UGlob.TVal; Next:TPElem END;

CL1List= CLASS {Определение класса}

PUBLIC {Общедоступные компоненты: поле CurVal и методы}

{ResetList,EOList,ExistNext,GetNext,InsPred,WriteAll и Create}

CurVal:UGlob.TVal; { отсюда можно взять информацию текущего }

{ элемента списка - "числитель и знаменатель" }

PROCEDURE ResetList;{Текущим устанавливается 1-й элемент }

{Его значение засылается в CurVal}

FUNCTION EOList: BOOLEAN;

{Проверяет наличие текущего элемента в списке}

FUNCTION ExistNext: BOOLEAN;

{Проверяет наличие следующего (за текущим) элемента в списке}

PROCEDURE GetNext;

{Текущим устанавливается следующий элемент списка}

{Его значение засылается в CurVal}

PROCEDURE InsPred(ValEl:UGlob.TVal);

{Вставляет перед(!) текущим новый элемент со значением ValEl}

{текущим остается тот, который был до, но предыдущим - новый}

PROCEDURE WriteAll(VAR TxtF:TextFile);

{Выводит в TxtF все элементы списка}

{!!! TxtF должен быть открыт для записи}

CONSTRUCTOR Create; { Конструктор - специализированный метод}

{для создания объектов типа CL1List }

PRIVATE {Приватные компоненты - поля}

PFirst{ссылка на 1-й},PCur{ссылка на текущий},

PPred{ссылка на предшествующий текущему - приходится хранить,}

{т.к. есть желание иметь возможность вставлять новый }

{элемент не после, а перед текущим}:TPElem;

END;

IMPLEMENTATION { В секции реализации описаны реализации методов }

{ класса CL1List.!!! Имена процедур и функций уточнены именем класса }

PROCEDURE CL1List.ResetList; BEGIN PPred:= NIL; PCur:=PFirst;

IF PCur<> NILTHEN CurVal:=PCur^.Inf

END;

FUNCTION CL1List.EOList; BEGIN EOList:=(PCur= NIL) END;

FUNCTION CL1List.ExistNext;

BEGIN ExistNext:=(PCur<> NIL)AND(PCur^.Next<> NIL) END;

PROCEDURE CL1List.GetNext; BEGIN

IF PCur<> NILTHENBEGIN PPred:=PCur; PCur:=PCur^.Next;

IF PCur<> NILTHEN CurVal:=PCur^.Inf

END

END;

PROCEDURE CL1List.InsPred; VAR p:TPElem; BEGIN

NEW(p); p^.Inf:=ValEl; p^.Next:=PCur;

IF PPred= NILTHEN PFirst:=p ELSE PPred^.Next:=p; PPred:=p

END;

PROCEDURE CL1List.WriteAll; VAR p:TPElem; BEGIN p:=PFirst;

WHILE p<> NILDOBEGIN

UGlob.WriteTVal(TxtF,p^.Inf); WRITE(TxtF,';'); p:=p^.Next

END; WRITELN(TxtF)

END {отметим, что при этом не "сбиты" значения PCur,PPred и т.д.};


CONSTRUCTOR CL1List.Create; BEGIN { Конструктор - }

{ специализированный метод для создания объектов типа CL1List, }

{ устанавливает значение списка пустым, }

{ но(!!!) его семантика не исчерпывается этим действием }

PFirst:= NIL;PCur:= NIL;PPred:= NIL

END;

END.

{Файл Farrej.DPR}

{$B-,D+,I+,Q+,R+}

PROGRAM Farrej; { Это программа построения ряда Фаррея F(n), по }

{ алгоритму: F(1) = 0:1,1:1; F(i) строится по F(i-1)}

{ вставками между каждой парой соседних элементов }

{ a:b и c:d, таких что (b+d)=i, нового }

{ элемента (a+c):(b+d). }

USES SysUtils,UGlob,L1List;

VAR RFarrej:L1List.CL1List { Объект RFarrej объявлен, но не создан };

PredCur,x:UGlob.TVal; FIn,FOut:TextFile; i,n:INTEGER;

BEGIN RFarrej:=L1List.CL1List.Create;

{ Вызов конструктора создает объект }

{ Имя конструктора уточняется именем класса,}

{ а ниже - имя объекта уже связано с объектом, т.к. он создан }

x.Ch:=0;x.Zn:=1;RFarrej.InsPred(x);

x.Ch:=1;{x.Zn:=1;}RFarrej.InsPred(x) {построили F(1)};

AssignFile(FIn,'FIn.TXT'); RESET(FIn); READ(FIn,n); CloseFile(FIn);

AssignFile(FOut,'FOut.TXT'); REWRITE(FOut); WRITELN(FOut,n);

FOR i:=2 TO n DOBEGIN {Строим F(i)}

RFarrej.WriteAll(FOut) {протокол для проверки - вывели F(i-1)};

RFarrej.ResetList {установились на начало списка};

REPEAT PredCur:=RFarrej.CurVal; RFarrej.GetNext{получили пару};

IF (PredCur.Zn+RFarrej.CurVal.Zn)=i THENBEGIN

x.Ch:=PredCur.Ch+RFarrej.CurVal.Ch; x.Zn:=i;

RFarrej.InsPred(x) {вставили новый}

END

UNTILNOT RFarrej.ExistNext;

END; RFarrej.WriteAll(FOut); CloseFile(FOut)

END.

Конструкторы и деструкторы.

Конструкторы и деструкторы являются специализированными методами класса. Синтаксис описания конструкторов и деструкторов отличается от синтаксиса описания процедур только ключевым словом CONSTRUCTOR и DESTRUCTOR вместо PROCEDURE. Имеется некоторая синтаксическая странность – конструктор описывается как процедура, а вызывается обычно как функция. Дело в том, что и в описании и в вызове имя конструктора уточняется именем класса, который и является типом значения, которое вырабатывает конструктор.

Назначение конструктора – «создать объект» и далее выполнить модифицированное тело. Назначение деструктора – выполнить модифицированное тело и далее «уничтожить объект». Из чего происходит их необходимость?

Описание переменной типа класс объявляет объект типа класс, но не создает его:

· в начальном приближении это можно понимать так – описание (статической) переменной всегда подразумевает выделение памяти для хранения ее значения, но не присваивает ей никакого «нормального» значения (это надо делать отдельно, с помощью оператора присваивания или ввода);


· более детально и точнее это можно понимать так – классы являются динамическими структурами данных (независимо от вида определения класса), а объекты динамическими переменными (или возможно, - их значениями), поэтому назначение конструктора – такое же, как у оператора NEW(ПеременнаяТипаСсылка).

· наконец, если пытаться объяснять смысл через его реализацию средствами более низкого уровня:

· переменная типа класс является классической статической переменной ссылочного типа, память ей выделяется как обычно, но значения она при этом не получает (тоже как обычно);

· поэтому, пока она не имеет значения, никак иначе ее нельзя использовать, кроме как одним из допустимых способов присвоить ей значение ссылки на объект (обычно, оператором NEW);

· только после этого эту переменную можно использовать для доступа к компонентам объекта.

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

СХЕМА, ИЛЛЮСТРИРУЮЩАЯ МЕСТОПОЛОЖЕНИЕ ФРАГМЕНТОВ ОБЛАСТИ ВИДИМОСТИ ОБЩЕДОСТУПНЫХ (PUBLIC) КОМПОНЕНТОВ КЛАССА.

Как уже говорилось выше, область видимости PUBLIC –компонентов состоит из 4 видов фрагментов:

a. в определении класса;

b. в описаниях реализации его методов;

c. в объектах класса;

d. аналогично, во всех классах-потомках и их объектах…

В нижеприведенной схеме отмечены фрагменты, где можно использовать имена Field, Metod1, Metod2 «видимых» компонентов класса TClass.

UNIT UnitTC;...

INTERFACE

...

TClass= CLASS...

PUBLIC

...

Field: ТипПоля;

Фрагмент вида (a)
PROCEDURE Metod1...

FUNCTION Metod2...

......

END;

...

VAR Variable1:TClass;...

IMPLEMENTATION

...

Фрагмент вида (b)
PROCEDURE TClass.Metod1;

... Field...

END;

FUNCTION TClass.Metod2;

... Metod1...

END;

...

VAR Variable2:TClass;...

Фрагмент вида (c)
...

Variable1.Field... Variable2.Field...

Variable1.Metod1... Variable2.Metod1...

Variable1.Metod2... Variable2.Metod2...

...

END.


UNIT...

USES... UnitTC...

...

VAR Variable3:UnitTC.TClass;...

Фрагмент вида (c)
...

Variable3.Field...

...

Variable3.Metod1... Variable3.Metod2...

...

END.

Кроме того, в любом из вышеприведенных модулей или другом модуле может присутствовать определение класса-потомка

TClass2= CLASS (UnitTC.TClass)...

PROCEDURE Metod3...

END;

в котором имена Field, Metod1, Metod2 не объявлены явно как имена компонентов, но эти компоненты наследуются, и поэтому согласно правилу (d) могут быть использованы

VAR Variable4:TClass2;...

IMPLEMENTATION

Фрагмент вида (d <- b)
...

PROCEDURE TClass2.Metod3;

... Field... Metod1... Metod2...

END;

...

VAR Variable5:TClass2;...

...

Фрагмент вида (d <- c)
Variable4.Field... Variable5.Field...

Variable4.Metod1... Variable5.Metod1...

Variable4.Metod2... Variable5.Metod2...

...

 

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...