ИмяПеременнойОбъекта.ИмяКомпонента
причем так осуществляется прямой доступ и к полям, и к методам, и к свойствам, если конечно они не инкапсулированы. ПРИМЕР. Вариант модуля для работы с однонаправленными линейными списками, использующий понятие класс, и пример использования этого модуля – построение ряда Фаррея. В нижеприведенном варианте модуля удалось устранить недостатки, отмеченные для вариантов, ранее рассмотренных в теме «Модули». · Теперь модуль 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: ТипПоля;
FUNCTION Metod2... ...... END; ... VAR Variable1:TClass;... IMPLEMENTATION ...
... Field... END; FUNCTION TClass.Metod2; ... Metod1... END; ... VAR Variable2:TClass;...
Variable1.Field... Variable2.Field... Variable1.Metod1... Variable2.Metod1... Variable1.Metod2... Variable2.Metod2... ... END. UNIT...
USES... UnitTC... ... VAR Variable3:UnitTC.TClass;...
Variable3.Field... ... Variable3.Metod1... Variable3.Metod2... ... END. Кроме того, в любом из вышеприведенных модулей или другом модуле может присутствовать определение класса-потомка TClass2= CLASS (UnitTC.TClass)... PROCEDURE Metod3... END; в котором имена Field, Metod1, Metod2 не объявлены явно как имена компонентов, но эти компоненты наследуются, и поэтому согласно правилу (d) могут быть использованы VAR Variable4:TClass2;... IMPLEMENTATION
PROCEDURE TClass2.Metod3; ... Field... Metod1... Metod2... END; ... VAR Variable5:TClass2;... ...
Variable4.Metod1... Variable5.Metod1... Variable4.Metod2... Variable5.Metod2... ...
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|