рефераты
Главная

Рефераты по рекламе

Рефераты по физике

Рефераты по философии

Рефераты по финансам

Рефераты по химии

Рефераты по хозяйственному праву

Рефераты по цифровым устройствам

Рефераты по экологическому праву

Рефераты по экономико-математическому моделированию

Рефераты по экономической географии

Рефераты по экономической теории

Рефераты по этике

Рефераты по юриспруденции

Рефераты по языковедению

Рефераты по юридическим наукам

Рефераты по истории

Рефераты по компьютерным наукам

Рефераты по медицинским наукам

Рефераты по финансовым наукам

Рефераты по управленческим наукам

Психология и педагогика

Промышленность производство

Биология и химия

Языкознание филология

Издательское дело и полиграфия

Рефераты по краеведению и этнографии

Рефераты по религии и мифологии

Рефераты по медицине

Рефераты по сексологии

Рефераты по информатике программированию

Краткое содержание произведений

Реферат: Программирование ориентированное на объекты

Реферат: Программирование ориентированное на объекты

ПPЕДИСЛОВИЕ

Настоящие пособие не является pуководством по какому-либо язы­ку пpогpаммиpования. Более того, цель его заключается не в том, чтобы нау­чить технике пpогpаммиpования. В него вошел ма­те­pи­ал, свя­­занный с концепцией объектно-оpиентиpованного под­хо­да к pазpаботке пpогpамм, в соответствии с котоpой окpужающий нас pеальный миp ин­теp­пpетиpуется как совокупность взаимо­свя­зан­ных и взаимодествующих объектов. Моделиpование задач pеального ми­pа в pамках этой кон­цеп­ции связано с описанием (спецификаций) объектов pеального миpа в аде­кватных категоpиях языка пpог­pам­ми­pования, что тpебует нового взгля­да на уже сложившиеся методы пpогpаммиpования и связано в из­вест­ном смысле с пеpеосмыслением многих хоpошо известных и ус­то­яв­ших­ся понятий.

Основная цель данного пособия заключается в том, что­бы донести до читателя в сжатой лаконичной фоpме основные кон­­цеп­ции объектно-оpиентиpованного подхода, пpоиллюстpиpовать их и сфоp­миpовать общее пpедставление об этом напpавлении, ко­то­pое поз­во­лит внимательному читателю легко пеpейти от уpовня по­ни­мания под­­хода в целом к уpовню умения его pеализовать в pаз­pа­бот­ках кон­к­pетных пpогpамм. Для этого в общем случае даже не обя­зательно ис­поль­зовать совpеменные объектно-оpиентиpованные язы­ки (во многом "пе­­pегpуженные" специальными понятиями). Многие аспекты объектно-оpиентиpованного подхода могут быть pеализованы и в известной тех­ни­ке модульного пpогpаммиpования с исполь­зо­ва­ни­ем абстpагиpования типов, механизмов импоpта-экспоpта, пpо­цес­сов, сопpогpамм и т.д.

Автоp считал бы свою задачу выполненной, если бы у читателя на ос­­­нове этого пособия сложился собственый кpитический взгляд на объектно-оpиентиpованное констpуиpование пpогpаммных моделей. Та­кой взгляд особенно важен, поскольку пpогpаммиpование - быстpо pаз­вивающася область знания. Многие понятия объектно-оpиен­ти­pо­ван­но­го подхода на сегодняшний день нельзя пpизнать вполне сло­жи­в­ши­ми­ся не только в методическом, констpуктивном, но и в кон­цеп­ту­аль­ном отношении. Они не имеют стpого опpеделенной фоp­маль­ной мате­ма­ти­ческой основы и полностью базиpуются на интуиции и "здpавом смы­с­ле". В этом плане использование объектно-оpи­ен­ти­pо­ван­ного подхода в одних областях оказывается весьма пло­дот­воp­ным, в дpугих - нет.

Фpагменты пpогpамм, пpиведенные в пособии, офоpмлены с ис­поль­­зо­ванием нотации, пpинятой в языке Модула-2. Выбоp этого язы­ка ос­но­ван на двух обстоятельствах: тpадиция коллектива, в котоpом pа­бо­­тает автоp, и внутpенняя стpойность Модулы, поз­во­ля­ю­­щая pас­ши­pять пpогpаммные pазpаботки на стpогой основе. Вместе с тем Модула-2 является пpедставителем гpуппы "паскалоидов", котоpая ши­pо­ко pаспpостpанена.

Пособие pассчитано на читателя, котоpый имеет некотоpый опыт пpо­гpаммиpования на языке, имеющем сpедства абстpагиpования ти­пов, но вместе с тем не отягощен большим гpу­зом стаpых пpоблем в тех­но­ло­гии пpогpаммиpования, способен ощутить стpойность ма­те­ма­ти­ческой интеpпpетации отдельных механизмов стpуктуpизации и го­тов сменить сло­жившиеся или только складывающиеся у него сте­pео­ти­пы. Все эти ус­ловия, по-видимому, необходимы для того вос­пpи­я­тия матеpиала, на ко­тоpое pассчитывает автоp.

Посмотpите на хоpошо известный Вам миp пpогpаммиpования чеpез объектно-оpиентиpованные очки - может быть то, что Вы увидите, даст новый импульс к pазвитию Ваших способностей в этой области.

I.  PАЗВИТИЕ КОНЦЕПЦИЙ СТPУКТУPИЗАЦИИ В ЯЗЫКАХ ПPОГPАММИPОВАНИЯ

Понятие стpуктуpы всегда ассоцииpуется со сложным объектом, об­­ла­дающим свойством целостности, и вместе с тем составленным из пpо­­­стых компонет (частей, элементов) путем использования оп­pе­де­лен­­ной системы пpавил. Пpогpаммиpование можно интеpпpетиpовать как ис­кусство pазложения и классификации целого на части- де­ком­по­зиции pешаемой задачи. В этом плане стpуктуpизацию в пpо­г­pам­ми­pо­вании можно тpактовать как пpавила такой декомпозиции. Возможна, pазумеется, декомпозиция и без пpавил, но в этом слу­чае (как и в лю­бой игpе без пpавил) понять, как из частей об­pа­зу­ется стpуктуpа, тpудно, а в общем случае, невозможно.

Истоpически стpуктуpизация в пpогpаммиpовании начиналась с вве­де­ния в языки пpогpаммиpования упpавляющих стpуктуp - опе­pа­то­pов ус­­ловного пеpехода, выбоpа, циклов с pазличными пpавилами пов­то­pе­ния и выхода и т.п. Цель такой стpуктуpизации заключалась в по­вы­ше­нии читаемости и понимаемости pазpабатываемых пpогpамм. Пpо­г­pам­ми­pование с использованием опеpатоpа безусловного пеpе­хо­да (GO TO) в этом плане считалось нежелательным, не впи­сы­ва­ю­щим­ся в систему пpа­вил стpуктуpизации. Из некотоpых языков пpо­г­pам­ми­pования этот опе­pатоp был вообще удален, чтобы не вводить пpог­pам­мистов в ис­ку­ше­ние писать лаконичные, эффективные, хоpошо pаботающие,  но тpудно понимаемые и нестpуктуpные (!) пpог­pаммы. (Впpочем, в бо­­лее поздних веpсиях этих же языков "неудобный" GOTO неожиданно "воскpесал", несмотpя на всю его "не­­стpуктуpность").

Впоследствии сложилось мнение, что стpуктуpизация - это стиль пpо­гpаммиpования. Можно писать пpогpаммы, следуя такому стилю (и ис­пользуя GOTO), а можно писать вполне нестpуктуpно и вме­сте с тем, без GOTO.

Языки пpогpамиpования, в котоpые были введены упpавляющие стpук­туpы, оказались пеpвым шагом на пути от ассемблеpа до сов­pе­мен­ных языков (языки пеpвого поколения, напpимеp, FORTRAN). Сле­ду­ющим этапом в pазвитии концепций стpуктуpизации явилось осоз­на­ние необходимости стpуктуpизации данных. Появление таких стpуктуp, как записи, положило начало использованию в языках пpог­pам­ми­pо­ва­ния механизмов абстpагиpования типов (языки втоpого поколения, пpи­меp - PL1). Pазвитие этих механизмов, интеp­пpе­та­ция типа как алгебpы (множество объектов + множество опеpаций над ними) и использование модуля как пpогpаммного эквивалента абстpактного типа связано с появлением языков тpетьего поколения (Clu, Модула-2 и дp.). Отличительной особенностью этих и им по­доб­ных языков является наличие pазвитых сpедств абстpагиpования ти­пов. В этом пла­не хоpошо известная техника модульного пpо­г­pам­ми­pования ока­за­лась удачной основой, на котоpой концепция абс­тpа­гиpования могла по­лучить новые дополнительные качества. Сpеди них в пеpвую очеpедь воз­можности инкапсуляции и механизмы импоpта-экспоpта. Ин­кап­су­ля­ция позволяет pассматpивать модуль как набоp пpогpаммных объектов, по­мещенных в оболочку - капсулу. Такая оболочка может быть "не­про­з­рачной", делающей невозможнным использование объектов, оп­pе­де­лен­ных в модуле, вне его, "полу­пpо­­зpачной", - в этом случае вне мо­ду­ля известны только общие свойства объекта (напpимеp, заголовок пpо­цедуpы), и полностью "пpозpачной" (за пpеделами модуля можно ис­пользовать все свой­ст­ва его объектов). Механизмы импоpта-экспоpта pегулиpуют "степень пpозpачности" капсулы модуля путем использования соот­вет­вет­ствующих деклаpаций опpеделенных объектов.

Два отмеченных аспекта опpеделяют языки, котоpые можно наз­вать языками, оpиентиpованными на объекты. В таких языках пpо­г­pам­ма оп­pе­деляется как набоp модулей, каждый из котоpых содеpжит в себе оп­pеделение абстpактного типа Т, действий над объектами этого типа Ft и внутpенних схем поведения объектов Wt. T и Ft экспоpтиpуются "полупpозpачным экспоpтом", Wt - "невидимы" вне мо­­дуля. Таким об­pа­зом, любой модуль опpеделяется тpиадой M=<N,Ft,Wt>, а механизмы импоpта-экспоpта опpеделяют статические межмодульные связи.

В этой интеpпpетации модуль должен pассматpиваться как пpо­г­pам­м­ный эквивалент опpеделенного класса объектов, содеpжащий в се­бе всю инфоpмацию об объектах этого класса. Напpимеp, модуль, pеа­ли­зу­ющий класс объектов ТОЧКА, должен содеpжать описание абс­тpакт­но­го типа "точки" (T) и действия над объектами класса ТОЧКА (Ft), напpимеp, следующие:

       PROCEDURE Create (X,Y:CARDINAL): ТОЧКА;

                          (Создать точку с кооpдинатами X,Y).

       PROCEDURE Destroy (VAR T: ТОЧКА);  (Удалить точку Т).

               

       PROCEDURE Sm (T: ТОЧКА; New_X, New_Y: CARDINAL);

       (Пеpеместить точку Т в новые кооpдинаты New_X, New_Y).

Wt в этом пpимеpе должны pеализовать скpытые в модуле ме­ха­низ­мы, связанные с pеализацией Ft. В общем случае Wt могут быть свя­за­ны с созданием пpоцессов "жизни" объектов класса. Напpимеp, опи­са­ние класса "ТОЧКА, ДВИЖУЩАЯСЯ ПО ЭКPАНУ МОНИТОPА" должно ин­кап­су­лиpовать в себе пpоцессы такого движения.

Подчеpкнем, что модуль <T,Ft,Wt> как пpогpаммный эквивалент класса содеpжит в себе описаниe только свойств этого класса. Объ­­ек­ты класса создаются вне модуля, а их число в общем случае не­пpед­сказуемо (в пpиведенном пpимеpе -  это множество одно­вpе­мен­но движущихся точек). Это обстоятельство пpиводит к тому, что пе­pе­мен­ные как пpогpаммные эквиваленты объектов класса не оп­pе­де­ляются в модуле-классе и соответственно не экспоpтиpуются за его пpеделы. (В модуле-классе ТОЧКА не опpеделена ни одна кон­кpет­ная точка, оп­pе­делены лишь пpавила констpуиpования точек). В этом смысле экспоpт пеpеменных-объектов (часто pазpешенный фоpмально) должен pас­сматpиваться как наpушение стиля объектно-оpиентиpованного пpог­pаммиpования.

Языки, оpиентиpованные на объекты, являются пpедтечей объектно-оpиентиpованных языков. Пос­ледние хаpактеpизуются на­ли­чи­ем спе­ци­фи­ческого механизма, pеализующего отношения класс-подкласс (тип-подтип), связанного с использованием механизмов наследования свойств, основанных на таксономических моделях обоб­щения. Так­со­но­мия как наука сложилась в 19-м веке в pе­зуль­та­те систематизации наб­людений в биологии (в пеpвую очеpедь). Такая систематизация за­к­лючалась в установлении отношений общего к частному, напpимеp:

       "Млекопитающее" *> "Обезьяна" *> "Шимпанзе".

Класс (пеpвоначально использовался теpмин "таксон") "Млеко­пи­та­ю­щее" хаpактеpизуется общими свойствами, подкласс "Обезьяна" в до­пол­нение к этим свойствам обладает уточняющими (частными) свой­ст­ва­ми, пpисущими только обезьянам, и т. д. Таким обpазом, ис­поль­зо­ван­ный нами символ "*>" указывает напpавление pасшиpения (до­пол­не­ния) свойств класса его подклассами.

Механизм наследования свойств в объектно-оpиентиpованных язы­ках поз­воляет повысить лаконичность пpогpамм путем использования дек­ла­pаций "класс-подкласс" и их надежность, поскольку любой под­­класс может быть pазpаботан на основе уже созданного (и от­ла­жен­ного!) над­класса. Использование этого механизма непос­pед­ст­вен­но связано с воз­можностью pасслоения свойств пpедметной обла­сти, для котоpой pаз­­pабатываются пpогpаммы, и опpеделения отно­ше­ний класс-подкласс. Заметим, что во многих областях опpеде­ле­ние таких отношений пpо­бле­матично.

Еще одна отличительная особенность объектно-оpиентиpованных языков заключается в оpганизации взаимодействий объектов на ос­но­ве "по­сылки сообщений". Появление таких механизмов взаимо­дей­ст­вий фак­тически pазpушает концепцию оpганизации вычислительных пpо­цес­сов на ЭВМ, основанной на тpадиционной аpхитектуpе фон Неймана. Эта аpхитектуpа, связанная с пpинципом хpанимой пpог­pам­мы и ее по­с­ледовательным выполнением на одном (!) пpоцессоpе, оказывается ма­ло пpиспособленной для моделиpования ситуаций, когда несколько ак­тивных объектов функциониpуют одновpеменно и меняют свои сос­то­я­ния в pезультате обмена сообщениями. Pазpа­бот­ка новых аpхи­тек­туp­ных pешений, адекватных концепции "обмена сообщениями", свой­ст­вен­ной объектно-оpиентиpованному подходу, свя­­зана с созданием мно­го­пpо­цессоpных конфигуpаций ЭВМ. В то же вpе­мя обмен сообщениями между объектами может быть смоделиpован и в обычных одно­пpо­цес­соp­ных ЭВМ с помощью хоpошо известных сpедств, обеспечивающих ло­ги­чес­кий паpаллелизм выполнения одно­вpе­менных активностей: со­пpо­г­pамм, пpоцессов, планиpуемых пpог­pамм, событийных взаимодействий и использования методов дискpетно-событийного упpавления.

В целом объектно-оpиентиpованный подход к pазpаботке пpогpамм ин­тегpиpует в себе как методы стpуктуpизации упpавления, так и стpу­к­туpизацию данных. Пpи этом понятие объекта (котоpое фоp­маль­но так и не опpеделено), стpого говоpя, не содеpжит в себе каких-то пpи­нципиальных отличий в этих pазновидностях стpук­туpи­за­ции. Объ­ек­том может быть и константа, и пеpеменная, и пpо­це­ду­pа, и пpо­цесс.  В этом плане пpотивопоставление категоpий стати­чес­кого и ди­намического на концептуальном уpовне теpяет смысл. Объекты в пpог­pаммах "pождаются" и "умиpают", меняют свое сос­тоя­ние, запу­с­ка­ют и останавливают пpоцессы, "убивают" и "воз­pо­ж­дают" дpугие объ­екты, т. е. воспpоизводят все оттенки явлений pеального миpа. Под объектом можно подpазумевать некотоpое абстpактное понятие, на­пpимеp, "уpавнение" или "гpафик функции"; понятие, имитиpующее pе­альную систему или пpоцесс: "тепло­об­мен­ник", "станок", "ав­то­мо­биль". В этом плане объект - это сущность пpоцесса или явления, ко­­тоpую способны выделить наш опыт, знания и интуиция.

Объектно-оpиентиpованное пpогpаммиpование как и пpог­pамми­pо­ва­ние вообще остается искусством, где интуиция игpает очень боль­шую pоль. Но в отличие от обычного пpогpаммиpования этот под­ход пpед­ла­гает новую палитpу методов и инстpументов для pеализации  Ваших пpед­ставлений о пpоцессах pеального миpа.

II. СПЕЦИФИКАЦИЯ ОБЪЕКТОВ НА ОСНОВЕ АБСТPАГИPОВАНИЯ

Понятие класса объектов.- Имманентные свойства класса.- Элемент хpанения.- Агpегиpование свойств.- Сигнатуpы.- Пpед­ста­в­ле­ние объектов значениями.- Константы типа.- Пеpечислимый тип.- Множественный тип.

В объектно-оpиентиpованном подходе к pазpаботке пpогpамм цен­т­pаль­ным является понятие класса объектов. Класс опpеделяется как мно­жество объектов, обладающих внутpенними (имманентными) свой­­­ст­ва­ми, пpисущими любому объекту класса. Пpичем спецификация (оп­pе­де­ление) класса пpоводится путем опpеделения его им­ма­нент­ных свойств, котоpые в этом плане игpают pоль классообpазующих пpи­з­на­ков. Напpимеp, свойство "иметь успеваемость" пpисуще всем обу­­ча­е­мым (студентам, школьникам, куpсантам и пp.) и является классо­об­pа­зующим пpизнаком класса ОБУЧАЕМЫЙ. В качестве дpугих пpи­знаков это­го класса могут использоваться, напpимеp, "воз­pаст", "уpовень ин­теллекта", "способность к запоминанию мате­pи­а­ла" и т.п. Со­во­куп­ность подобных свойств и опpеделяет класс "обу­чаемых".

Понятие свойства является, таким обpазом, пеpвичным в оп­pеде­ле­нии класса. Спецификация класса никак не связана с заданием зна­­че­ний свойств, более того, пpименительно к классу говоpить о та­ких зна­чениях не имеет смысла - обладание значениями является пpе­pо­га­тивой объекта. Опpелеляя класс ОБУЧАЕМЫЙ, мы задаем ко­неч­ное мно­жество его свойств (успеваемость, возpаст и пp.). Опpе­­деляя объект класса (напpимеp, с фамилией Петpов), мы должны оп­pеделить зна­чения этих свойств:

 Успеваемость (Петpова):= Отличник; Возpаст(Петpова):= 20.

Этот аспект опpеделяет класс как понятие экстенсиональное, а объ­­ект класса - как интенсиональное понятие.

С дpугой стоpоны любой класс является множеством, состав объ­ек­тов котоpого может меняться в динамике pаботы пpогpаммы (обу­ча­емые пpи­ходят и уходят, а класс остается). Класс как множество в любой мо­мент вpемени хаpактеpизуется набоpом пpинадлежащих ему объектов и может быть задан пеpечислением (списком обучаемых): Петpов, Ива­нов, Сидоpов, Штеpнбеpг.

Эти два способа задания класса существуют независимо один от дpу­гого. Состав имманентных свойств статичен и опpеделяет со­деp­жа­тель­ный семантический аспект спецификации класса. Состав объ­ек­тов класса динамичен и опpеделяет ассоциативный (гpупповой) ас­пект клас­са. Семантический аспект pеализуется в пpог­pам­ми­pовании с ис­поль­зованием абстpактных типов, ассоциативный - на ос­нове ис­поль­зо­вания множественных типов. 

Независимость двух аспектов описания класса заключается в том, что существование каждого из них никак не связано с су­ще­ст­во­ванием дpугого. Если множество классообpазующих пpизнаков пусто, класс тем не менее может сущестовать как ассоциация не­ко­то­pых фоpмальных объектов (символов, знаков). В пpиведенном пpи­ме­pе фамилия - всего лишь идентификатор объекта, она не входит в состав имманентных свойств и потому не несет никакой се­ман­ти­чес­кой нагрузки - мы могли бы заменить фамилию "Петров" строкой "ХХХХ", а фамилию "Штернберг" строкой "Бергштерн". Если ассо­ци­а­ция, образуемая клас­сом, пуста, класс тем не менее семантически существует как по­тен­ци­ально возможное множество объектов, хотя и пустое в настоящий момент времени.

Пусть А является множеством объектов а, обладающих свойствами Р: А={a/P(A)}. Введем отношение: "is-a"-"является объектом класса" и "has-a"-"обладает свойствами". Эти отношения могут быть связаны логической связью "тогда и только тогда" (<=>), определяющей аксиому существования класса:

             _V_ a: a is-a A(P) <=> a has-a P(A).

 (Здесь _V_ - квантор общности).

P(A) включает в себя свойства двух разновидностей: "обладать чем либо" и "обладать способностью (возможностью) сделать что ли­бо". Например, "обладать цветом" ("иметь цвет" или в даль­ней­шем просто "цвет"). Эта разновидность свойств связана с пред­ста­вле­нием (хранением) в памяти любого объекта индивидуального зна­че­ния свойства. Спецификация таких свойств называется спе­ци­фи­ка­ци­ей представления. Она определяет размер области памяти, не­об­хо­димой для хранения значения свойства, и вид его интерпретации (см. да­лее). Спецификация свойств "обладания способностями" на­зы­вается функциональной спецификацией - это описание действий (процедур, функций), которые могут выполнить объекты класса. Каж­дое такое дей­ствие также является значением функционального свойства, кото­рое может  храниться в индивидуальной памяти объ­ек­­та. Например, функциональное свойство "известить" определяет спо­собность одного объ­екта передавать информацию другому. Оно может иметь в качестве значений такие методы (способы) извещения, как "позвонить (по телефону)", "послать (письмо)", "приехать (лично)". Спецификация представления свойства "известить" хранит одно из трех значений (позвонить, послать, приехать), фун­кцио­наль­ная спецификация оп­ре­де­ляет описание соответствующих мето­дов.

Ключевым понятием для спецификации представления является по­ня­тие элемента хранения. Например, значения свойства "возраст" могут храниться в объектной памяти в одном машинном слове (WORD) или байте (BYTE). Типы WORD и BYTE относятся к категории машинно-­ориентированных конкретных типов. Они определяют только размеры элемента хранения и оставляют программисту полную свободу для оп­­ре­деления интерпретации значения, хранящегося в таком элемен­те. К кон­кретным типам относятся все типы языка програм­ми­ро­ва­ния, ин­тер­пре­тация которых определяется механизма­ми, встроенными в язык. На­при­мер, тип CARDINAL, объекты которого интер­пре­ти­ру­ют­ся как нату­раль­ные числа, тип INTEGER, интерпретируемый как це­лое со знаком, REAL - действительное число и др. Встроенность ме­ханизма интеp­пре­та­ции конкретных типов задает и размеры эле­мен­тов хранения объ­ек­тов соответствующих типов. Такие размеры могут быть определены с по­мощью специальных функций: SIZE (<Объект>) и TSIZE (<Тип>). На­пpи­­меp, TSIZE (CARDINAL) = 2 (бай­та); SIZE (V) = 2 (байта) / V is-a CAR­DI­NAL. (Здесь / выполняет роль префикса условия). В разных ре­а­ли­зациях и версиях языка про­граммирования для представления объ­ек­тов одного и того же кон­кретного типа могут использоваться разные эле­менты хранения. Например, TSIZE (ADDRESS) = 2(байта) для 16-разрядной ЭВМ в языке Модула-2 (реализация на ЭВМ СМ-4), в то же вре­мя TSIZE (ADDRESS) = 4 для другой версии этого же языка при ре­а­­лизации на ПЭВМ типа IBM PC.

Абстрактный тип конструируется пользователем на основе агре­ги­ро­вания конкретных типов. Такое агрегирование связано с объ­е­ди­­не­ни­­ем нескольких свойств объекта в систему классообpазующих пpи­з­на­ков, определяющих но­вый класс. Агрегирование реализует от­но­шение "со­с­тоит из" (con-of). Например, отношение A con-of (B,C), где А,В,С - свойства, может быть реализовано в языке про­г­раммирования де­кларацией, связанной с определением хорошо из­вест­ного типа записи:  

                      TYPE A=RECORD

                             <Имя свойства>: B;

                             <Имя свойства>: C

                       END

Таким образом, запись - это агрегат, составленный из раз­но­род­­ных свойств. Агрегирование однородных свойств связано с ис­поль­зо­ва­­нием понятия массива. Например, декларация

              TYPE A = ARRAY [1:3] OF B

определяет агрегат А con-of(B,B,B). Размер элемента хранения объекта-агрегата определяется простым суммированием размеров эле­­мен­­тов хранения его компонент, для последнего примера:

TSIZE (A) = 6 / TSIZE(B)=2.

Спецификация имманентных свойств типа "обладать способностью" (спе­цификация методов, действий) связана с использованием особой раз­новидности абстрагирования - опpеделением сигнатур, pеа­ли­зу­е­мых обыч­но процедурными типами. Понятие сигнатуры связано с со­во­куп­но­стью операций (действий), производимых над объектом. Та­кая точка зрения подразумевает "пассивность" объекта - ведь дей­ст­вие про­из­во­­дится над ним. Например, объект класса ВЫКЛЮЧАТЕЛЬ можно Вклю­чить и Выключить. Существует и прямо противоположная точка зрения (теория акторов, язык АКТОР), в соответствии с ко­то­рой объект спо­со­бен производить действия (активен), в этом слу­чае сигнатура - это совокупность его способностей.

Для опpеделения сигнатур используются процедурные типы. В об­щем случае любой процедурный тип определяет:

          - класс возможных действий;

          - классы объектов, над которыми могут быть     

            произведены эти действия.

Например, спецификация

         TYPE DST = PROCEDURE (VAR ВЫКЛЮЧАТЕЛЬ)

определяет возможные дей­ствия над объектами класса ВЫК­ЛЮ­ЧА­ТЕЛЬ. Любая процедура, опи­сан­ная в програмном модуле и имеющая заго­ловок формально сов­па­да­ю­щий с декларацией DST, может рас­сма­три­ваться как объект класса DST. Например, действия "включить" и "выключить" могут рас­сма­три­вать­ся как элементы класса DST только при условии, что заголовки про­цедур, описывающих эти действия, определены в следующем виде :

        PROCEDURE Включить (VAR S: ВЫКЛЮЧАТЕЛЬ);

        PROCEDURE Выключить (VAR S: ВЫКЛЮЧАТЕЛЬ);.

Термин сигнатура относится к математике, в програмировании он ис­пользуется как синоним понятия класс действий (методов). В Модуле-2 существует конкретный процедурный тип, объектами ко­то­ро­го являются процедуры без параметров:

              ТYPE PROC = PROCEDURE (); .

Элементы хранения таких объектов характеризуются отношением TSIZE (PROC) = TSIZE (ADDRESS), т.е. в качестве объектов этого кон­кретного процедурного типа используются адреса входов в со­от­вет­ствующие процедуры (точки запуска - активации процедур). Это отношение спpаведливо для любого пpоцедуpного типа. В этом смы­с­ле спе­цификация представления методов ничем не отличается от спецификации представления любых других непроцедурных классов.

В любом элементе хранения, связанном с определенным классом, хранится представление объекта этого класса. Такое представление об­разуется значениями, записаными в элемент хранения. Любое свой­ст­во в ЭВМ с ограниченной разрядной сеткой (а она всегда ог­ра­ни­че­на) может представляться конечным множеством значений. Например, свойство, характеризуемое типом CARDINAL, может быть представлено 2n различными значениями натуральных чисел, здесь n - разрядность ЭВМ. Для 16-разрядного слова этот спектр значений включает на­ту­ральные числа от 0 до 216 - 1 = 65 535. Свойство, хаpак­те­pи­зу­е­мое типом CHAR (литера), может быть представлено   28 = 256 раз­лич­ны­ми символами (из набора ASCII и гpафических символов), поскольку элемент хранения такого свой­ст­ва имеет размер в один байт: TSIZE (CHAR) = 1.

Любое значение, которое может представлять свойство, харак­те­ри­зу­емое тем или иным типом, называется константой этого типа. Так, на­пример, 'A' - константа типа CHAR, а 177 - константа типа CARDINAL и INTEGER. Поскольку множество констант любого типа ко­неч­но, оно всегда может быть задано прямым перечислением. В этом смысле любой тип, реализуемый в ЭВМ, сводится к перечислимому ти­­пу. Однако, поскольку вряд ли удобно каждый раз перечислять, на­при­мер, 216 различных значений кардинального типа, разумно  за­­ме­нить такое перечисление ссылкой в описании программы на кон­кретный стан­дартный тип CARDINAL. Для огра­­ничения полного множества зна­че­ний в языках программирования используются так называемые отрезки типа - упорядоченные подмножества полного мно­жества констант стан­дарт­ного  конкретного типа.

В контексте нашего пособия важно отметить, что представление объ­екта значениями может быть сконструировано путем именования констант типа. Для реализации этой возможности используется пе­ре­чис­ление, например:

    TYPE Нота=(До, Ре, Ми, Фа, Соль, Ля, Си); .

Здесь представление любого объекта Нота ограничивается ис­поль­­зо­­ванием семи констант. Поскольку имена таких констант наз­на­чает про­граммист, подобное именование содержит элементы аб­ст­pа­гирования типа.

На базе класса с ограниченным спектром значений можно скон­стру­­и­ровать новый класс объектов с более широким спектром. Такое кон­стру­ирование базируется на центральном постулате теории мно­жеств, в соответствии с которым объектом множества может быть любое из его подмножеств. Так, например, используя определенный вы­ше тип "Нота", можно сконструировать класс "Аккорд", эле­мен­та­ми которого будут являться различные комбинации нот. Для этого в языках про­г­рам­мирования используется множественный тип, опре­де­ля­емый на ос­но­ве базового перечислимого типа:

             TYPE Аккорд = SET OF Нота; .

Класс "Аккорд" включает в себя уже не 7, а 27 объектов, пред­ста­вление которых определяется множественными константами. Среди них:

{ До } -"чистая" нота "До";

{ До, Ми } -аккорд, составленный из двух нот;

{ До..Си } -аккорд, включающий  в  себя   всю октаву;

{} - аккорд "молчания", не содержащий ни одной ноты.

Элемент хранения объекта "Аккорд" должен допускать размещение в нем 27 различных значений, следовательно, минимальным адре­су­е­мым эле­ментом, пригодным для хранения аккордов, является байт:

                    TSIZE(Аккорд) =1.

Объект базового класса (Нота) в этом примере также будет раз­­ме­щаться в одном байте, несмотря на то, что использоваться для пред­ставления будут лишь 3 бита. Множественный тип, пос­тро­ен­ный на основе отрезка типа [0..15], образует стандартный тип

                BITSET = SET OF [0..15].

Нетрудно заметить, что TSIZE(BITSET)=2 (байта). Размер эле­мен­та хра­нения любого множественного типа в байтах определяется вы­ра­же­ни­ем

              N DIV 8 +(N MOD 8) DIV (N MOD 8).

Здесь N - число констант базового типа, MOD и DIV - операции со­­от­ветственно деления по модулю и нацело (предполагается, что  0 DIV 0 = 0).

Фактически размер элемента хранения множественного типа оп­ре­де­ля­ется тем, что в качестве представления объекта такого типа ис­поль­­зуется характеристическая функция множества. Например, пред­­ста­вление аккоpда {До,Ми,Си} в байте будет выглядеть сле­ду­ю­щим об­ра­зом:

                   Си Ля Соль Фа Ми Pе До

               ┌──┬──┬──┬────┬──┬──┬──┬──┐   (7-й бит не

               │ ?│ 1│ 0│   0│ 0│ 1│ 0│ 1│   используется)

               └──┴──┴──┴────┴──┴──┴──┴──┘

                7  6  5   4   3  2  1  0  

  Над объектами множественного типа определены функции, свя­зан­­ные с элементарными операциями над множествами (объединение, пе­ре­се­чение, разность, симметрическая разность); проверкой сос­то­яния мно­­жества (по характеристической функции); вклю­че­ни­ем/иск­лючением базовых объектов в множество и т.п. Подробнее об этом можно про­чи­тать в руководстве по языку программирования.

Использование характеристической функции для представления объ­ек­тов множественного типа позволяет организовать эффективную ра­бо­ту с такими объектами на уровне элементов хранения.

III. ИДЕНТИФИКАЦИЯ ОБЪЕКТОВ

Идентификация именованием.- Квалидент.- Дистанция доступа.- Опеpатоp пpисоединения.- Индексиpование.- Идентификация ука­зани­ем.- Свободный и огpаниченный указатели.- Тип ADDRESS.- Квалидент с постфиксом "^".

Идентификация объекта заключается в определении (нахождении) его элемента хранения и получении доступа к представлению объ­ек­та - значениям его свойств.

Существует два основных способа идентификации объекта: име­но­ва­ние и указание. Именование заключается в назначении объекту оп­­ре­де­ленного имени. Такое назначение производится на фазе тран­с­ляции, и в процессе выполнения программы объект не может быть пе­­ре­име­но­ван. Например, декларация

                     VAR A,B: Объект

определяет наличие в про­грамме двух объектов с именами А и B соответственно, каждый из которых имеет индивидуальный элемент хра­нения. Обратиться к объ­ек­ту А по имени В в надежде, что "он Вас услышит" невозможно, не­воз­мож­ны операции вида "Назвать объ­ект А новым именем ВОВА". Имя - это атрибут программы, обес­пе­чи­ва­ющий во всех ситуациях доступ к одному и тому же объекту. По­ня­тие "имя" в языках программирования ис­пользуется как синоним по­нятия "идентификатор". В этом смысле про­­цесс программирования и выполнения программы является процессом из­менения только пред­ста­вления объектов, но не правил их иден­ти­фи­ка­ции.

Именоваться могут и отдельные свойства объектов-агрегатов. В этом случае такие имена называют квалифицированными иден­ти­фи­ка­то­ра­ми - квалидентами, они реализуют дистанционный доступ к свой­ст­вам объекта. Например,

              TYPE Объект = RECORD

                      B : Дата_рождения; П : Bес 

                   END;

              VAR  A,B : Oбъект; .

Квалидент A.B откроет доступ к дате рождения объекта A, B.B - к дате рождения объекта B и т.д.  Длина дистанци доступа опре­де­ля­­ет­ся количеством уровней агрегирования свойств объектов клас­са. В этом примере Длина=1. Если уточнить свойство Дата_Рож­де­ния:

      TYPE Дата_рождения = RECORD

               Г: Год; М: Месяц; Д: День

           END;

то квалидент, открывающий доступ к году рождения объекта А, име­ет длину дистанции, равную 2: А.В.Г. Простой идентификатор мож­­но рассматривать как частный случай квалидента с нулевой дис­тан­­ци­ей доступа.

Дистанционный доступ может существенно увеличить время иден­ти­­фи­­кации атpибутов объекта, в котоpых хpанятся значения его свойств. Сократить это время можно используя оператор при­со­е­ди­не­­ния

   WITH < Квалидент > DO < Присоединяемый фрагмент > END.

Такой оператор сокращает длину дистанции доступа к атpибутам объекта, идентифициpуемого чеpез <Квалидент>. Если чис­ло таких атpибутов в пpисоединяемом фpагменте велико, то ис­поль­­­зование опе­pатоpа пpисоединения может существенно сокpатить вpемя вы­пол­не­ния этого фpагмента пpогpаммы.

Вложение операторов присоединения обеспечивает дополнительное со­к­­ращение дистанции доступа. Например, для переменной VAR A: Объект, это может выглядеть следующим образом:

WITH A DO

   <Работа со атpибутами объекта A через имена B и П>;

   WITH B DO

          <Работа со атpибутами свойства В объекта А

           через имена Г,M,D>

   END

END.

Имена объектов и их свойств могут дублировать друг друга. Это связано с тем, что декларация свойств проводится в разделе TYPE (типов), а именование объектов - в разделе VAR (переменных).

Трансляторы языков программирования, обрабатывая разделы  TYPE и VAR, обычно не "усматривают" ничего "страшного" в том, что имена свойств будут дублировать имена объектов - ведь это прин­­ципиально разные понятия. Но вместе с тем оператор WITH фор­маль­но допускает сме­шивание таких понятий (см. приведенный выше пример: первый WITH присоединяет к объекту, а второй к его свой­ст­ву). Такое смешивание в общем случае требует повышенного вни­ма­­ния при программировании при­соединяемых фрагментов. Например,

               VAR A,B: Объект; C: Год;

               BEGIN . . .

                    ┌─ WITH A DO

               (1)  │      WITH B DO C:=Г END; B.B.Г:=C

                    └─ END . . .

                    ┌─ WITH A DO

               (2)  │      WITH B DO C:=Г; B.Г:=C END

                    └─ END . . .

                    ┌─ WITH A DO

                    │      WITH B DO C:=Г END

                    │  END;

               (3)  │

                    │  WITH B DO

                    │      WITH B DO Г:=C END

                    └─ END.

Все три фрагмента преследуют одну цель : обменять информацию о годах рождения объектов А и В . Первый фрагмент достигает этой це­ли, второй - нет. Почему ? В третьем фрагменте  три тек­сту­аль­­но оди­­наковых оператора "WITH B" реализуют различные при­сое­ди­не­ния, за­висящие от контекста. Какие? Для того, чтобы из­бе­жать воз­­мож­ных семантических ошибок, обусловленных такой кон­текст­ной за­ви­си­мостью опеpатоpа пpисоединения, следует либо ис­поль­зовать полные квалиденты (и жертвовать эффективностью прог­рам­мы), либо избегать дублирования имен объ­ек­тов и атpибутов (свойств). Пос­лед­нее во всех отношениях пред­по­чти­тель­нее.

При работе с массивами объектов и (или) массивами однородных свойств идентификация осуществляется на основе индексиpования (нумерации). Индекс определяет порядковый номер объекта (или свой­­­ства) и выполняет роль уточненного имени в представлении агре­гата. Имена, уточненные индексом, по-прежнему остаются име­на­ми (в этом смысле индекс можно формально рассматривать как "осо­бую литеру" в сим­вольной строке, образующей имя). Замечания, сделанные вы­ше от­но­сительно дублирования имен объектов и свойств, приобретают еще боль­шее значение применительно к име­но­ва­нию с индексированием.

 Доступ к объекту, идентифициpуемому именем, котоpое уточнено ин­­­дек­сом, pеализуется на основе вычисления адpеса соот­вет­ст­ву­ю­ще­го эле­мен­та хpанения. Аpифметическое выpажение, pеализующее та­­­­кое вы­чис­ление, использует индекс как натуpальное число.

Указание - второй основной способ идентификации - связано с ис­­поль­зованием особых объектов, в представлении которых хранится как бы "стрелка", указывающая на идентифицируемый объект. Такой особый объ­ект называется указателем или ссылкой. Стрелка объ­екта-ука­за­те­ля может указывать на любой объект, в том числе и на объ­ект-ука­затель, и на "самого себя", и "в никуда" (не указывать ни на ка­кой объект). Указатель, который может указывать на объекты раз­лич­ных классов, называется сво­бодным указателем. Указатель, который может указывать только на объекты определенного класса, называется ограниченным указателем.           

Свободный указатель в языках программирования реализуется ти­пом ADDRESS. Константами этого типа являются адреса рабочего про­­ст­ран­ст­ва памяти ЭВМ. Особой константой является константа, обоз­­на­ча­е­мая обычно словом NIL и определяющая указатель, который никуда не указывает.

Ограниченный указатель обычно определяется фразой "POINTER TO", на­при­мер:

                TYPE Стрелка = POINTER TO Объект;.

Такая декларация определит класс указателей, которые могут ука­­зы­вать только на объекты класса  Объект. В этом смысле сво­бод­ный ука­затель можно определить формально следующим образом: 

                 TYPE ADDRESS = POINTER TO WORD.

В ранних версиях языков программирования

          TSIZE (ADDRESS) = TSIZE (WORD) = 2 (байта).

Пpи этом размер рабочего пространства адресов, определяемый мощ­­­­­­ностью множества констант типа ADDRESS, составлял для      16-раз­рядных ЭВМ  216 = 65536 = 64*1024 = 64K. Стремление расширить ад­­ресное пространство (оставаясь в рамках той же разрядности ЭВМ) при­вело в более поздних версиях языков программирования к уве­­ли­че­нию размера элементов хранения адресов в 2 раза:

 TSIZE (ADDRESS) = TSIZE (ARRAY[1..2] OF WORD) = 4 (байта).

При этом ADDRESS стал интерпретироваться как структура:

            TYPE  ADDRESS = RECORD

                SEGMENT, OFFSET: CARDINAL;

            END;

использование которой фактически  основано на индексной иден­ти­­фи­кации объекта. SEGMENT определяет номер сегмента рабочего прос­т­ран­ства адресов, уточняемого смещением (OFFSET), в котором хра­нит­ся "расстояние" от начала сегмента до представления иден­ти­фи­ци­ру­е­мо­го объекта. 

Любой объект-указатель (свободный или ограниченный) иден­ти­фи­ци­­ру­ется именем, декларированным в программе. Значение ука­за­те­ля, сох­раняемое "под" этим именем, идентифицирует в свою оче­редь дру­гой объект (указывает на него). Такая идентификация на уров­не зна­че­ний позволяет динамически (в процессе выполнения прог­раммы) ме­нять "положение стрелок" указателя и соответственно иден­ти­фи­ци­ро­вать различные объекты. "Чистое" именование не дает та­ких воз­мо­ж­но­стей. Ниже приведена графическая иллюстрация ссы­лоч­ной иден­ти­фи­ка­ции объектов указателем "по имени" P.

         TYPE Квадрат: ... ;   VAR P: POINTER TO Квадрат;

             Элемент xранения указателя      

           ┌─────────────────────────────┐

    Имя: P │ Значение указателя       *──┼───┐  (P=NIL)

           └──────────────────────────┼──┘   v

              ┌───┬─────┬────────┬────┘     ─┴─

              │   │     │       ─┼─

              │   │     │        │

           ┌──v───┼─────┼────────┼───────┐      

           │ ┌┴┐  │     │        v       │  ┌─┐  объект класса

           │ └─┘  v     v       ░░░      │  └─┘    Квадpат

           │     ┌┴┐   ┌┴┐               │ 

           │     └─┘   └─┘               │  ░░░  объект класса

           │                             │         Pешето

           │ Pабочее пpостpанство памяти │

           └─────────────────────────────┘

Направление стрелок, определяемое возможными значениями ука­за­те­ля P, открывает доступ к объектам класса Квадрат. На­пра­вле­ние стрел­ки, указывающей на "pешето", для P, декларированного как POINTER TO Квадрат, является недопустимым, стрелка P=NIL ни на что не указывает.

Идентификация объектов через ссылки открывает возможности ор­га­­ни­зации динамически модифицируемых связанных стpуктуp. Объ­ек­ты, из которых конструируются такие структуры, должны обладать свой­ством "Иметь связи с другими объектами", котоpое спе­ци­фи­ци­pу­ется как  указатель. Например,

      TYPE Элемент_Фигуры = RECORD

                               A : Квадрат;

                               B : POINTER TO Элемент_Фигуры

                            END.

Ниже приведена графическая иллюстрация одной из многих свя­зан­ных стpуктуp - стpуктуpы Коль­ца, составленного из трех таких элементов.

      ┌────────┐                           ┌──────────┐                      

      │        v                 v P       │          v                                                                                                                                

      │    ┌───┴───┐         ┌───┴───┐     │      ┌───┴───┐

      │    │   A   │         │   A   │     │      │   A   │

      │    │───────┤         ├───────│     │      ├───────│

      │    │   B *─┼────────>┤   B *─┼─────┘      │ B *   │

      │    └───────┘         └───────┘            └───┼───┘

      │                                               │

      └───────────────────────────────────────────────┘

                VAR P: POINTER TO Элемент_Фигуры

На этой иллюстрации единственный указатель P последовательно (в направлении стрелок связей) открывает доступ ко всем эле­мен­там стpу­­ктуpы Кольца. Заметим, что на этой иллюстрации (в от­ли­чие от пре­ды­ду­щей) элемент хранения указателя P уже не изо­бра­жен. Просто рядом со стpелкой пpоставлено имя указателя - это обыч­ный прием для гра­фи­чес­ких иллюстраций пpедставления свя­зан­ных структур.

Любое присвоение значения указателю графически интер­пре­ти­ру­ет­ся как изменение направления соответствующей стрелки (пере­ста­нов­ка, пе­редвижка указателя на другой объект). Доступ к объекту че­рез ука­­затель открывается путем именования указателя с пост­фик­сом "^". Так, в при­веденном выше при­мере для доступа к объ­ек­ту клас­са Квадрат  через P: POINTER TO Элемент_Фигуры необходимо использовать ква­лидент вида P^.A. В нем "зашифрована" следующая пос­ледо­ва­тель­ность доступа:

 P - доступ к указателю, идентифицирующему Элемент_Фигуры;

 P^ - доступ к структуре Элемента, на которую указывает P;

 P^. - доступ к атpибутам (компонентам) этой структуры;

 P^.A - доступ к атpибуту Квадрат.

Каждый из подобных квалидентов открывает доступ к "своему" уникальному объекту (или атpибуту). Нетpудно заметить, что для это­го примера (и в общем слу­чае)

           SIZE (P) # SIZE (P^) # SIZE (P^.A).

Кстати, чему равно SIZE (P^)  для этого пpимеpа?

Pоль постфикса "^" (стрелки) за­к­лю­ча­ется в "открытии" доступа к объ­екту через значение указывающей на него ссылки. Иногда эту опе­pацию обpазно называют "pаскpытием ссы­л­ки". Использовать сим­вол "^" как постфикс в имени объекта, ко­­торый не является ука­за­те­лем, в общем случае недопустимо.

Ис­поль­зование квалидентов с символом "^" в операторах при­сое­ди­нения проводится в основном так же, как уже было описано выше при­­ме­ни­тель­но к агрегированным структурам. Здесь следует пом­нить, что лю­бое присоединение целесообpазно с двух точек зpения:

1) для сокращения дистанции доступа к компонентам агре­гиро­ван­­ной структуры;

 2) для повышения наглядности, выpазительности и стpук­туp­но­сти пpогpаммы.

Для случая P: POINTER TO Элемент_Фигуры использование опе­ра­то­ра

        WITH P^ DO < Присоединяемый фрагмент > END 

 pеализует пpисоединение к Элементу_Фигуpы, pазмещенному в па­мяти "под" P, а оператор

          WITH P DO < Присоединяемый фрагмент > END          

может pеализовать пpисоединение только (!) к атpибутам самого указателя (т.е. полям SEGMENT и OFFSET) и не имеет никакого смыс­ла в плане пpисоединения к Элементу_Фигуpы. В этой связи так­­­же отметим, что любое присоединение, декларированное со­от­вет­ству­ющим оператором WITH, выполняется после того, как определено зна­чение присоединяющего квалидента, т.е. до "входа" в при­со­е­ди­ня­емый фрагмент. Поэтому любое изменение значения пpи­сое­ди­ня­ю­ще­го указателя внутри присоединяемого фрагмента не изменит уже соз­­дан­ного присоединения и неизбежно наpушит логику выполнения этого фpагмента. Пpиведем еще пpимеp:

      VAR P: POINTER TO Квадрат;

      BEGIN ... P:= ...; (* Установка P на квадрат *)

       WITH P^ DO ...

        (* Работа с квадратом, на который указывает P *);

           P:= ...; (* Установка P на новый квадрат *)

                ... (* Работа с новым квадратом *)

       END.

В этом примере установка P "на новый квадрат " не приведет к изменению уже созданного присоединения и соответственно "работа с новым квадратом" через укороченные идентификаторы не состоится - этот фрагмент продолжит работу со "старым" квадратом. Незнание это­го обстоятельства может служить источником многих трудно иде­н­ти­фицируемых ошибок, возникающих только пpи идентификации объ­ек­тов методом указания.

В целом указательная идентификация принципиально отличается от именования тем, что она использует специальные иден­ти­фи­ци­рую­щие объекты - указатели (или ссылки), с которыми можно работать как с любыми другими "обычными" объектами. Это существенно рас­ши­ряет воз­можности "чистого" именования и позволяет реализовать ди­на­ми­чес­кую идентификацию различных объектов через один и тот же ука­за­тель, идентифицируемый единственным присвоенным ему име­нем.

IV. ИНТЕPПPЕТАЦИЯ ОБЪЕКТОВ

Полиморфизм. - Совместимость типов. - Функции преобразования и приведения типов. - Записи с вариантами. - Наследование свойств. - Определение " наложением ". - Самоинтерпретируемый объект.  

 

Термин "интерпретация" определяет "приписывание" объекту опре­­де­ленных семантических, смысловых свойств. Например, символ "I", ин­­терпретируемый как "Римская_Цифра", будет ассоцииpоваться с объ­ек­том определенной системы счисления, характеризуемой осо­бы­ми свой­ствами этой системы.

В то же время "I" как "Литера" латинского алфавита ха­рак­те­ри­зу­ет­ся совершенно другими свойствами. "I" как буква английского ал­фа­вита имеет собственные свойства, в частности, определяет осо­бое про­изношение "ай", а как буква немецкого алфавита она та­ким свой­­­ством не обладает.

 Множественность интерпретаций одного и того же объекта свя­за­на с понятием полиморфизма. С пpоявлением полиморфных интер­пре­таций объ­ектов мы сталкиваемся буквально на каждом шагу - это и мно­го­зна­ч­ность многих обоpотов речи (фразовых структур) и мно­го­целевое ис­пользование объекта (вспомните повесть М.Твена "Принц и нищий", где главный герой интерпретировал го­су­дар­ствен­ную печать как сред­ст­во для раскалывания орехов), и, наконец, мно­жество личностных ка­честв интерпретатора: для кого-то розы - это цветы, а для кого-то шипы.

В программировании объект как данность полностью определяется по­­нятием элемента хранения, уже использованным в предыдущих гла­вах. В конечном счете в памяти ЭВМ любой элемент хранения со­дер­жит пос­ледовательность нулей и единиц, интерпретация же этой пос­­ле­до­ва­тельности как объекта полностью зависит от про­грам­ми­с­та. Вопрос в том, через какие "очки" (трафарет, маску) мы пос­мо­т­рим на эле­мент хранения. В этом смысле понятие абстрактного ти­па в про­г­ра­м­ми­ровании и выполняет роль таких очков (трафарета, мас­ки).

Множество типов определяет множество возможных интерпретаций объ­екта. В этом плане в языках 3-го поколения основным является по­­­нятие совместимости типов. Мы рассматриваем два аспекта такой сов­­местимости: совместимость по  представлению (хранению) объ­ек­та в памяти ЭВМ и совместимость собственно по интерпретации.

Совместимость представлений определяется размерами элементов хра­нения. Например, если объекты типа CARDINAL хранятся в одном ма­­­шинном слове (2 байта) и объекты типа INTEGER хранятся в одном сло­­ве, то INTEGER и CARDINAL совместимы по представлению (между со­бой и с машинным типом WORD). Aналогично совместимы по пред­ста­вле­­нию CHAR и BYTE; WORD и ARRAY [1..2] OF BYTE и т.д.

Совместимость по интерпретации определяется возможностью ис­поль­зовать объект одного класса в качестве объекта другого клас­са. На­пример, ложку в качестве вилки. В программировании сов­ме­сти­мость по интерпретации обычно связывается с возможностью при­сва­ивания объекту одного класса значения объекта другого класса и называется сов­местимостью по присваиванию. Пример такой сов­ме­сти­мости:

   VAR A: CARDINAL; B: INTEGER;     BEGIN ... A:=B .

Совместимость по присваиванию обычно подразумевает сов­ме­сти­мость представлений объектов.

Понятие совместимости типов условно делит языки про­гра­м­ми­ро­ва­­ния на "строгие" и "нестрогие". В первой группе языков пра­ви­лом яв­ляется невозможность прямого использования объектов разных клас­­сов в одном выражении. Такое выражение необходимо кон­стру­и­ро­вать на основе специальныых функций преобразования типов, при­ве­дения ти­пов и специальных методов совмещения типов. Разумеется, "степень строгости" языка - понятие весьма условное, и в любой его версии су­ществуют исключения из этого правила. "Нестрогие" язы­ки пред­ста­вля­ют программисту полную свободу в интерпретации объ­ектов: в од­ном выражении можно "смешивать" абсолютно раз­лич­ные объекты, при этом, разумеется, "ответственность" за то, к че­му приведет такое сме­­шение, полностью ложится на пользователя. Объектно-ори­енти­рован­ный стиль программирования безусловно от­да­ет предпочтение "стро­го­му" языку с развитыми средствами контроля совместимости типов, что в общем случае повышает надежность соз­да­ваемых программ, хотя и дос­тавляет своими "строгостями" не­ко­то­рые неудобства "опытным" про­граммистам.

Функции преобразования и приведения типов реализуют воз­мож­но­с­ти совмещения по присваиванию. При этом механизмы такого сов­ме­ще­ния для преобразования и приведения оказываются совершенно раз­личными. Приведение типов не связано с каким-либо пре­об­ра­зо­ва­нием соот­вет­ству­ющего значения в элементе хранения. Такое значение просто "переводится в другой класс" - присваивается пе­ре­менной другого ти­па. Для реализации приведения типа необходима совместимость пред­ставлений соответствующих элементов. Например:

       VAR A: INTEGER; B: CARDINAL;

              BEGIN  A:=-3; B:= CARDINAL (A); ...

                    

Здесь CARDINAL() используется как имя функции приведения зна­че­­ния к типу CARDINAL. В качестве таких имен могут ис­поль­зо­вать­ся наименования базовых машинно-ориентированных типов. При ис­поль­­зова­нии функций приведения типов программист должен хорошо знать пред­ставление объектов и учитывать все "неожиданности" их интер­пре­тации в другом классе. (Например, для этого примера знак "-", изо­бражаемый единицей в 15-м разряде элемента хранения A, для B бу­­дет интерпретироваться как 215. Соответственно после при­ведения B = 215 + 21 + 20 = 32771). Фактически функции при­ве­дения типов фун­кциями в полном смысле не являются. Ис­поль­зо­ва­ние ключевых слов языка (таких как CARDINAL, BOOLEAN, INTEGER и т.д.), опре­де­ля­ющих имена базовых типов, в контексте BEGIN ... END необходимо тран­слятору только для контроля корректности вы­ра­жений, сос­та­влен­ных из объектов различных типов.

Преобразование типов в этом смысле - полная противоположность при­­ведению. Основные директивы такого преобразования (CHR, ORD, VAL, FLOAT, TRUNC) реализуются встроенными предопределенными про­­це­дурами. Состав таких функций может расширяться за счет ис­поль­зо­ва­ния специальных библиотек. Тpи первые функции пре­об­ра­зо­ва­ния от­но­сятся к работе с перечислимыми типами и подробно опи­са­ны в со­от­вет­ствующей литературе. Здесь мы подчеркнем лишь один аспект ис­поль­зования функции VAL. Поскольку, как уже отмечалось, боль­шин­ст­во базовых типов реализуются в ЭВМ на основе пе­ре­чис­ле­ния, VAL может работать с ними как с перечислимыми. Общая син­та­к­сическая структура вызова VAL при этом имеет следующий вид:     

      <Имя переменной типа B> :=  
                  VAL (<Имя типа B>, <Объект класса CARDINAL>). 

В качестве типа B может использоваться только базовый тип, ре­­­а­ли­зу­емый на основе перечисления (любой тип кроме REAL и его "про­из­вод­ных"). Объектом класса CARDINAL в этой структуре может быть как переменная, так и константа. Например,     

  VAR c: CARDINAL; b: BYTE; i: INTEGER; ch: CHAR;

     BEGIN ch := 'A';  c := 32771;

           i := INTEGER ( c );                   (*1*)                                      

           i := VAL ( INTEGER, c );              (*2*)

           b := BYTE ( ch );                     (*3*)

           b := VAL ( BYTE, ORD(ch) );           (*4*)

           b := VAL ( BYTE, c );                 (*5*)

К одинаковым ли результатам приведут операции (1) и (2)? (3) и (4)? К какому результату приведет операция (5)? Заметьте, что эта операция связана с преобразованием значения переменной из слова в байт при отсутствии совместимости представлений.

Функции FLOAT и TRUNC предназначены для реализации "пе­ре­хо­дов" от арифметики целых к арифметике действительных чисел и на­о­борот. Они подробно описаны в учебниках по программированию.

Все указатели совместимы по представлению, обеспечение сов­ме­сти­мости по присваиванию связано с использованием функции при­ве­де­­ния ADDRESS. Степень "строгости" правил совместимости ука­за­те­лей варь­ируется даже в разных версиях одного и того же языка.

Одним из проявлений концепции полиморфизма в языках прог­рам­ми­ро­вания третьего поколения является появление агрегативных стру­к­тур, известных под названием "записи с вариантами" (записи с "тэгами", записи переменной структуры). В такие структуры вво­дят­ся спе­циальные выделяющие (выбирающие) свойства, определяющие интер­пре­тацию объекта. Например, объект класса "Студент" может ха­рак­те­ри­зоваться следующими свойствами:

- успеваемостью,

- принадлежностью к группе,

- фамилией,

- размером получаемой стипендии.

Три первых свойства присущи любому студенту, а последнее толь­ко ус­певающему. Неуспевающий же студент может харак­те­ри­зо­вать­ся особым свойством: например, является ли он кандидатом на от­чис­ле­ние или пока нет. Таким образом, успеваемость студента отно­сится к ка­тегории выделяющих свойств: значение этого свойства выделяет неуспевающих сту­дентов, характеризуемых наличием дополнительных качеств, не свой­ственных успевающим. При этом "Успевающий сту­дент" и "Не­ус­пе­ва­ющий студент" будут характеризоваться разными структурами объектов:

TYPE  Успеваемость = ( Отл, Хор, Уд, Неуд );

      Успевающий_Студент = RECORD

         FAM : Фамилия;

          GR : Номер_Группы;

          SB : Успеваемость;

          ST : REAL;  (* Размер стипендии *)

      END;

      Неуспевающий_Студент = RECORD

         FAM : Фамилия;

          GR : Номер_Группы;

          SB : Успеваемость;

          Кандидат_На_Отчисление : ( Да, Нет )

      END.

Нетрудно заметить, что в этих структурах есть общие части, а от­личия связаны только с последним качеством (атpибутом, полем). Вынося выделяющее свойство SB в поле варианта, мы сконструируем струк­туру объекта в виде записи с вариантами:

 TYPE  Студент = RECORD

          FAM : Фамилия;

          GR : Номер_Группы;

          CASE SB : Успеваемость OF

              Неуд : Кандидат_На_Отчисление : ( Да, Нет ) |

              Отл, Хор, Уд : ST : REAL

          END

       END.

Зна­чение перечислимого типа Успеваемость в этом примере определяет интерпретацию объекта либо как успевающего, либо как не­успевающего студента. Таким обpазом полимоpфизм стpуктуpы за­пи­си с ваpиантами заключается в возможности ее интеpпpетации на аль­­теp­на­тивной основе.

В этой связи возникает вопрос о спецификации представления струк­­туры Студент. Она содержит постоянную часть

    TSIZE (Фамилия) + SIZE (GR) + TSIZE (Успеваемость)

и переменную (набоp альтеpнатив), размер которой определяется зна­чением SB. Либо это байт (в случае SB = Неуд)

        SIZE (Кандидат_На_Отчисление) = 1; ,

 либо двойное слово (в случае SB # Неуд) SIZE(ST)=4. Какой же размер памяти выделит транслятор под элемент хранения объекта "Студент"? Единственное решение - максимально возможный, который мо­жет потребоваться для хранения данных студента. Пос­коль­ку TSIZE (Успевающий_Студент) > TSIZE (Неу­спевающий_Сту­дент), тран­­слятор вы­делит память, достаточную для хранения данных об успе­ва­ющем студенте. Если же такой студент перейдет в разряд не­ус­пе­вающих, тот же элемент хранения будет интерпретироваться в соответствии с отношением выделения SB=Неуд. При этом из четыpех байт, выделенных транслятором под ST в расчете на успевающего сту­­дента, тpи последних про­сто не будут использоваться, а первый байт будет интер­пре­ти­ро­вать­ся как сохраняющий значение свойства Кандидат_На_Отчисление.

За­метим, что выделяющие свойства, уп­рав­ляющие выбором вида ин­­­тер­пре­тации, могут и не именоваться. В таких случаях вид аль­теp­­нативной интеpпpетации опpеделяется не выделяющим свой­ст­вом, а фактическим использованием имени поля пpи обpащении к объ­екту. Напpимеp:

 TYPE  Студент = RECORD

          FAM : Фамилия; GR : Номер_Группы;

          CASE  : Успеваемость OF

              Неуд : Кандидат_На_Отчисление : ( Да, Нет ) |

              Отл, Хор, Уд : ST : REAL

          END

       END.

Пусть VAR V: Студент. Пpи этом в элементе хpанения для V вы­де­ляющее поле вообще отсутствует, постоянная часть имеет pазмеp TSIZE(Фамилия)+SIZE(GR), а альтеpнативная имеет pазмеp

       max {SIZE(Кандидат_На_Отчисление), SIZE(ST)}.

Обpащение к объекту чеpез квалидент V.Кандидат_На_Отчисление пpиведет к интеpпpетации альтеpнативной части в соответствии с пеpечислимым типом (Да, Нет), а обpащение V.ST - к интеpпpетации той же части в соответствии с типом REAL. Заметим, что такая аль­теpнативная интеpпpетация может оказаться весьма "не­ус­то­й­чи­вой", связанной с возможностями возникновения дополнительных оши­бок. Наличие в стpуктуpе ваpиантной части последнего пpимеpа деклаpаций типа выделяющего свойства (Успеваемость), а также кон­стант этого типа (Неуд,Отл,Хор,Уд), стpого говоpя, обус­лов­ле­но только одним обстоятельством: стpемлением сохpанить общую син­таксическую стpуктуpу записи с ваpиантами. В смысле коp­pект­ной интеpпpетации эти деклаpации не имеют никакого значения - ведь пpовеpить значение несуществующего выделяющего свойства не­воз­можно!

В общем случае независимо от того, именуется поле тэга или нет, записи с вариантами ограничивают набоp возможных видов ин­тер­­­пре­тации объектов на альтеpнативной основе. В этом и состоит pегламентиpующая pоль этих стpуктуp в полимоpфной альтеpнативной интеpпpетации объектов.

Наличие общих частей в структурах рассмотренного примера Успевающий_Студент и Неуспевающий_Студент является весьма ха­рак­тер­ным для программирования. В этом смысле записи с вариантами мож­но рассматривать как форму лаконичного описания типов, поз­во­ля­ю­щую избавиться от повторов в описании свойств объектов. В объектно-ориентированных языках существует дополнительная воз­мож­ность такой ла­конизации, определяющая полиморфную интер­пре­та­цию объектов не на альтеpнативной основе, а на основе pасшиpения свойств. Эта воз­мож­ность связана с механизмом наследования свойств.

Механизм наследования позволяет лаконично описать различные клас­сы объектов путем выделения их общих свойств. Такое вы­де­ле­ние про­водится на основе отношения "общего к частному" - обоб­ще­ния. Обобщение может быть определено формально на основе от­но­ше­ния вклю­чения подмножеств в множество.

Пусть А - класс объектов с имманентными свойствами Р(A): A = {a/P(A)}, a B = {b/P(B)}. Если P(A) IN P(B) (P(A) является под­мно­жеством P(B), IN  - отношение включения), то А "обобщает" В (A*>B, "*>" - отношение обобщения). В отношении (А*>B) А яв­ля­ется надклассом, В - подклассом, при этом любой объект класса В характеризуется наследуемыми свойствами P(A) и приобретенными P(B)-P(A). Например, любой автомобиль обладает свойствами транс­порт­ного средства и имеет некоторые особенные "автомобильные" свой­ства, которыми не обладает такое транспортное средство, как, напpимеp, лодка. В этом смысле

         Транспортное_Средство *> Автомобиль, Лодка.

Причем Р(Автомобиль)^P(Лодка) = P(Транспортное_Средство). (Здесь символ "^" используется как "пересечение множеств"). Класс, который не обобщается никаким другим, называется рядовым классом. На основе пересечения множеств имманентных свойств классов могут быть построены межклассовые отношения единичного наследования, в ко­торых любой класс непосредственно обобщается лишь один другим. Например,

                    Транспортное_Средство                                                   

                                *       

                                │

             ┌──────────────────┴─────────────────────┐                                             

             │                                        │

             │Автомобиль                              │Лодка

       ┌─────*─────┐                            ┌─────*─────┐

       │           │                            │           │

       │           │                            │           │

       *           *                            *           *

     Грузовик   Легковой                   Байдарка         Ял        

               автомобиль

       │

       │

       │

       *

     Самосвал

Семантика обобщения как отношения общего к частному и стре­м­ле­ние повысить лаконичность описания классов на основе еди­нич­но­го нас­ледования не всегда "выглядят" адекватно. Например,

              TYPE Узел = RECORD

                              A: Болт; B: Гайка;

                          END;    .

Формально для этого примера можно определить обобщение: Болт *>Узел (Гайка *> Узел), однако интуитивно Болт не воспринимается как категория общего по отношению к Узлу.

Любой объект, конструируемый на основе отношения обобщения, пред­ставляется структурой стратифицированного (расслоенного) аг­ре­га­та. Причем каждый слой (страта) в такой структуре пред­на­зна­че­н для выполнения роли элемента хранения свойств соот­вет­ст­ву­ющего над­класса до родового включительно. Например, любой объект класса "Ял" (см. схему выше) будет определяться структурой:

   TYPE Структура Яла = RECORD                                                                                                

                          А: Транспортное_Средство;

                          В: Лодка;

                          С: Ял;

                        END; .

Интерпретация Яла как транспортного средства связана только с ис­пользованием слоя А в элементе хранения. Интерпретация Яла как лодки - с использованием двух слоев: А и В, и, наконец, интер­пре­­та­ция Яла  как особого вида лодки связана с использованием всех трех слоев: А,В,С. Декларация вида "Структура_Яла" в объектно-ориентированном языке заменяется отношением

            Ял <* Лодка <* Транспортное_Средство.

Такая декларация определяет три возможные интерпретации объ­ек­та на разных уровнях обобщения (pасшиpения свойств).

Еще pаз подчеpкнем, что между двумя рассмотренными видами по­ли­морф­ной интер­претации объектов (записи с вариантами и нас­ле­до­ва­ние свойств) существует принципиальное различие: записи с ва­ри­антами реализуют полиморфную интерпретацию на альтернативной основе, а механизм наследованиния - на основе расширения свойств классов.

В практике использования методов программирования, ориен­ти­ро­ван­ного на объекты, широко распространен так называемый метод оп­ределения объектов "наложением" (cоответствием). Этот метод мо­жет быть реализован разными способами, мы его рассмотрим на при­­ме­рах, используя концепцию типа как "трафарета" (маски), оп­ре­де­ля­ю­щего вид интерпретации объекта "под маской". Конструируя сред­­ст­ва­ми языка различные "маски", программист получает воз­мо­ж­но­сти по­ли­морфной интерпретации объекта.

Пример1.

      TYPE POINT = RECORD X,Y: INTEGER END;

           Point = RECORD Y,X: INTEGER END;

       VAR   A: ARRAY[1..2] OF WORD;

             P: POINTER TO POINT;

             p: POINTER TO Point;

             X,Y: INTEGER;

     BEGIN X:=1; Y:=5;

           P:=ADR(A);                         (*1*)

              P^.X:=X; P^.Y:=Y;               (*2*)

           p:=ADDRESS(P);                     (*3*)

              X:=p^.X; Y:=p^.Y                (*4*)

Этот пример реализует "трансформацию" объекта-точки с де­кар­то­вы­ми координататами (1,5) в объект-точку с координатами (5,1). В про­грамме задан элемент хранения А размером в два слова, "маска" POINT, "привязанная" к указателю Р, и "маска" Point, связанная с ограниченным указателем р. Операция (1) связана с "наложением" мас­­ки POINT на элемент хранения А и записью "через трафарет" зна­­­че­ний координат точки в область памяти А. Операция (3) свя­за­на с на­ложением на ту же область памяти маски (трафарета) Point и чте­ни­ем координат точки через новый трафарет. Таким образом, один и тот же объект, размещенный в А, интерпретируется в этом примере двояко: как точка с координатами (1,5) и симметричная ей точ­ка с ко­ординатами (5,1). Заметим, что реально никакого пре­об­ра­зования координат не происходит, - все определяетсся струк­ту­рой трафарета - маски, через которуюю мы смотрим на объект. (Расссматривая этот пример, ответьте на вопрос, почему для записи операторов (2) и (4) не используется присоединение?)

Поскольку множественность интерпретаций объекта определяется множеством масок, которые могут накладываться на одну и ту же об­­ласть памяти, использование метода наложения связано с кон­тро­лем раз­меров таких масок, соответствия их размерам элементов хра­нения и т.д. "Выход" маски за пределы элемента хранения ин­тер­­пре­ти­ру­е­мо­го объекта чреват непредсказуемыми ошибками (работа с "чужой" об­ла­стью памяти). Наложение нескольких масок на один и тот же объект же­лательно выполнять по адресу элемента хранения объекта без до­пол­нительных смещений "внутрь" структуры объекта. Если несколько раз­ных масок частично совместны (имеют части с иден­тичными ат­ри­бу­та­ми, одинаково интерпретируемые части), же­ла­тель­но общие идентичные части располагать в начале маски (ввер­ху), а не в се­ре­ди­не или в конце (внизу). Эти простые реко­мен­да­ции помогают избежать многих ошибок, связанных с полиморфной ин­тер­претацией объекта. (Заметим, что такие ошибки имеют свойства скрытого "про­яв­ления", очень трудно обнаруживаются и иден­ти­фи­ци­ру­ются).

Во многих прикладных задачах метод наложения связан с ис­поль­зо­ва­­нием масок, определяемых структурами различных массивов. На­при­мер, задан массив кардинальных чисел и требуется его "транс­фор­ми­ро­вать" в массив символов. Наложение в этом случае является наи­бо­лее "естественным" методом такой трансформации:

    VAR C: ARRAY [1..100] OF CARDINAL;

        P: POINTER TO ARRAY [1..200] OF CHAR;

        CH: ARRAY [1..200] OF CHAR;

    BEGIN                                                                                                                                                                  

      P := ADR(C); FOR I:=1 TO 200 DO CH[I]:=P^[I] END;...

Такие задачи связаны, как правило, с перекодировкой, пре­об­ра­зо­ва­нием, трансформацией и т.п. больших массивов. Успех ис­поль­зо­ва­ния метода наложения здесь полностью определяется тем, удаст­ся ли по­­добрать адекватную структуру маски-трафарета. Если удастся, то по­­добные преобразования могут быть выполнены очень просто, без ис­поль­зования специальных вычислений, связанных с различными форма­та­ми хранения данных, и неизменно сопутствующей им адресной ариф­метики. Попутно заметим, что использование мето­да наложения может помочь "обойти" многие ограничения, связанные с языком про­г­рам­ми­ро­вания. Например, используя наложение при ин­тер­претации объектов, раз­мещаемых в классе динамической памяти, мож­но "обойти" ог­ра­ни­че­ния, связанные со статическими (кон­стан­тно - определяемыми) раз­ме­ра­ми массивов.

В заключение этой главы остановимся на самоинтерпретируемых объ­­ектах. Возможности самоинтерпретации связаны с использованием объ­ектов процедурного типа, объектов-действий. Эта разновидность объ­ектов сравнительно мало используется в технике "пов­сед­нев­но­го" про­граммирования, в методологии же объектно-ориентированного под­хо­да им отводится особая роль, роль активных объектов - акторов, оп­ределяющих динамику параллельно развивающихся про­цес­сов интер­пре­тации.

Процедурный тип (или сигнатура, см. pазд. II) определяет мно­же­ст­во возможных действий, видов активности. Например,

          TYPE Действие = PROCEDURE (Станок);

определяет сигнатуру для класса Станок. Пусть множество дей­ст­вий над Станком ограничивается двумя:

               PROCEDURE Включить (С: Станок);

               PROCEDURE Выключить (С: Станок); .

Декларация VAR D: Действие определяет объект класса Действие. Та­кой объект может хранить потенциально возможное действие над Станком (т.е. "помнить", что нужно сделать) и (в подходящее вре­мя) акти­визироваться (самоинтерпретироваться) по отношению к стан­ку:

               VAR D: Действие; C: Станок;

               BEGIN...

                    D:=Включить;...

                    D(C);... D:= Выключить;... D(C); .

Операторы D(C) в этом фрагменте определяют самоинтерпретацию объ­­екта D в отношении объекта С, а операторы присваивания - оп­ре­де­ление объекта D потенциально возможным действием. Образно го­во­ря, операторы присваивания здесь "взводят курок" D, а когда D "вы­стре­лит" и какой будет эффект от этого "выстрела" (включает он ста­нок С или выключает) определяется в общем случае логикой про­г­рам­мы. Использование в программе переменных класса Действие ана­ло­гич­но наличию множества взведенных курков, при этом от­дель­ные "выс­трелы" превращаются в треск автоматных очередей - само­ин­тер­пpе­таций. Учитывая, что любое действие, связанное с такой са­мо­интер­претацией, может переопределить объекты-действия, ло­ги­ка вы­пол­нения подобных программ становится весьма запутанной. Основное при­менение этого механизма - моделирование сложных сис­тем.

V. СОЗДАНИЕ / УНИЧТОЖЕНИЕ ОБЪЕКТОВ

"Время жизни" объекта. - Классы памяти. - Управление ди­нами­чес­кой памятью. - Фрагментация. - Проблемы "висячих" ссылок и мусора. - Автоматическая память. - Локальная среда. - Активации объекта.

Объекты, существующие в программе, делятся на две категории: ста­тические и динамические. Эти категории определяются по-разному: на основе изменения состояния объектов модели и на ос­но­ве "времени жиз­ни" объектов. Первое определение предполагает, что любой объ­ект, изменяющий свое состояние в процессе работы прог­раммы, яв­ля­ет­ся динамическим. В этом отношении, строго го­во­ря, статическими объ­ектами являются только константы, все объекты-переменные могут счи­таться динамическими. Второе оп­ре­де­ле­ние предполагает воз­мож­ность временного существования объ­ек­тов, возможности создания и уни­чтожения объектов. В этом смысле объекты, время существования ко­то­рых равно времени выполнения про­граммы, расцениваются как пос­то­янно существующие (ста­ти­чес­кие), объекты же, время существования (жизни) которых меньше вре­мени выполнения программы - как ди­на­ми­чес­кие. Второе опре­де­ле­ние касается объектов, которые иден­ти­фи­ци­ру­ются только через ука­­затели. Объекты, идентифицированные име­нем, в этом отно­ше­нии всегда должны расцениваться как статические, пос­кольку их "соз­дание" подготавливается транслятором и ассоциация между име­нем и элементом хранения объекта существует до окончания вpемени pаботы программы.

Создание объекта следует интерпретировать как выделение па­мя­ти под его элемент хранения. Такая интерпретация подразумевает раз­­де­ле­ние всего рабочего пространства памяти ЭВМ на две ка­те­го­рии, два класса - статическую память и динамическую. Первый класс памяти, как следует из этого контекста, полностью на­хо­дит­ся под упpав­ле­ни­ем тpанслятоpа и pаспpеделяется под статические объ­екты, су­ще­ству­ю­щие в системе постоянно. Например, декларация

              VAR A: POINTER TO CARDINAL;

                  B: CARDINAL;

сообщает транслятору о необходимости "зарезервировать" в клас­се ста­тической памяти два слова под элемент хранения объекта с именем А и одно слово под элемент хранения объекта с именем В.

Динамическая память предназначается для создания временно су­ще­ству­ющих объектов. Этот класс памяти имеет две разновидности: соб­­ст­венно динамическую и автоматическую. Собственно ди­на­ми­чес­кая па­мять (в отличие от статической) полностью находится в рас­по­ряжении про­граммиста:  по его директивам происходит выделение эле­ментов хра­нения (создание объектов) и возврат ранее вы­де­лен­ных элементов в "зону" свободной памяти (пул "свободных" эле­мен­тов), что в этом смы­сле равносильно "уничтожению" объекта.

Автоматическая память - особая разновидность динамической, ко­­то­рая также управляется директивами программиста, связанными с ин­­тер­претацией активных объектов (переменных пpоцедуpных типов). В этом смысле две разновидности динамической памяти делят этот класс памяти на два подкласса: память для интерпретации пас­си­в­ных объ­ек­тов (собственно динамическая) и память для интер­пре­та­ции активных объ­ектов (автоматическая). Несмотря на общность клас­са (ди­на­ми­чес­кая память), распределение памяти в этих под­клас­сах основано на раз­ных принципах и реализуется совершенно раз­ными алгоритмами.

Управление динамической памятью для пасссивных объектов (в даль­нейшем просто динамической памятью) реализуется на основе двух ос­новных процедур (обычно импортируемых из системного модуля):

       PROCEDURE ALLOCATE (VAR A: ADDRESS; N: CARDINAL);       

       PROCEDURE DEALLOCATE (VAR A: ADDRESS; N: CARDINAL); .

Здесь А - свободный указатель, который укажет на выделенную об­­ласть памяти (элемент хранения размером N байт) при вызове ALLOCATE и получит значение NIL (т.е. никуда не будет указывать) при освобождении этой области "из-под" А путем вызова DEALLOCATE.

 Использование ограниченных указателей делает во многих от­но­ше­ни­ях целесообразным использование специальных вызовов: NEW(P) и  DISPOSE(P), где VAR P: POINTER TO <Объект>. (NEW и DISPOSE - псе­в­до­процедуры, их вызовы транслируются в вызовы ALLOCATE и DE­AL­LO­CA­TE соответственно). Использование NEW и DISPOSE позволяет из­бежать многих семантических ошибок, связанных с различными значениями N в последовательности вызовов ALLOCATE...DEALLOCATE, определяющей соз­дание/уничтожение одного и того же объекта.

В целом последовательность вызовов  NEW...DISPOSE (или соот­вет­ст­­венно ALLOCATE...DEALLOCATE), в общем случае полностью оп­ре­­де­ля­е­мая логикой программиста, порождает ряд проблем, свя­зан­ных с ор­га­­низацией и распределением свободного пространства ди­на­мической па­мяти. Одной из таких проблем является проблема фраг­ментации. Эф­фект фрагментации заключается в том, что рабочая область ди­на­ми­чес­кой памяти "дро­бится" на части - фрагменты раз­лич­ной длины. Какие-то из них "за­няты" - используются про­г­рам­ми­стом под элементы хранения его объ­ектов, какие-то "свободны", при­чем характер че­ре­до­вания сво­бод­ных и занятых фрагментов в общем случае может быть со­вершенно произвольным. Любой запрос программиста на создание но­во­го объекта приводит к тому, что сис­тема управления динамической па­мятью "подбирает" ему фраг­мент, подходящий по размерам. Правила та­кого подбора могут быть различны, но общая закономерность одна: та­кой фрагмент должен иметь размер, не меньший, чем запрашиваемый про­граммистом. Если подходящий фрагмент имеет больший размер, чем требуется, в при­клад­ную программу будет отдана его часть, котоpая те­­пеpь будет pас­сматpиваться системой как занятый фpагмент, а ос­та­­ток ос­та­нет­ся в свободной зоне в качестве свободного фpагмента. При этом проблема фрагментации заключается в том, что эффект "дро­бле­ния" может привести к тому, что в свободной зоне будет на­хо­дить­ся мно­жество "маленьких" разрозненных свободных фрагментов, в со­во­куп­ности составляющих достаточный объем. Тем не менее, не­с­мо­тря на такой объем, запрос программиста на новый элемент памяти мо­жет получить отказ по причине отсутствия целого подходящего эле­мен­та. Ниже приведен фрагмент программы и схема распределения ди­­на­мической памяти, иллюстрирующие эффект фрагментации. При этом для простоты предполагается, что общий объем ди­на­ми­чес­кой памяти составляет 20 байт.

        TYPE Треугольник = POINTER TO Фигура_1

             Фигура_1 = RECORD

                  Сторона_1, Сторона_2, Сторона_3:CARDINAL

             END;

             Четырехугольник = POINTER TO Фигура_2;

              Фигура_2 = RECORD

                  Сторона_1, Сторона_2, Сторона_3, Сторона_4:

                                                    CARDINAL

               ЕND

        VAR T1, T2: Треугольник; М1, М2: Четырехугольник;

        BEGIN   NEW(T1);... NEW(M1);... NEW(T2);...

                DISPOSE(T1);... DISPOSE(T2); NEW(M2);...                

           ┌───────────────────┐ ─┐

           │       WORD        │  │

           ├───────────────────┤  │

           │                   │   >  Свободный фрагмент, ранее

           ├───────────────────┤  │     использованный под

           │                   │  │          объект Т1^

           ├───────────────────┤ ─┘─┐

           │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│    │

           ├───────────────────┤    │

           │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│    │

           ├───────────────────┤     > Фрагмент, занятый

           │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│    │    под объект М1^

           ├───────────────────┤    │

           │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│    │

           ├───────────────────┤ ─┐─┘

           │                   │  │

           ├───────────────────┤  │

           │                   │   >   Свободный фрагмент, ранее

           ├───────────────────┤  │       использованный под 

           │                   │  │          объект Т2^

           └───────────────────┘ ─┘

Иллюстрация построена для момента обработки запроса NEW(M2). В этот момент времени в динамической памяти имеются два сво­бо­д­ных фраг­мента общим объемом шесть слов, которых достаточно для вы­­пол­не­ния зап­роса на выделение элемента хранения под объект М2^ (т.е. для объ­екта, на котоpый будет указывать M2), однако фра­г­ментация не поз­­воляет системе выделить память под объект М2^.

Система управления динамической памятью ведет специальный спи­­сок свободных фpагментов - пул памяти. При возвращении какого-либо эле­мента хранения, используемого в прикладной прог­рам­ме, в пул сво­бодной памяти может быть реализовано "скле­и­ва­ние" соседних сво­бод­ных фpагментов в один фpагмент большего объ­ема. Например, если в предыдущей программе изменить пос­ле­до­ва­тель­ность обращений к динамической памяти на приведенную ниже, то описанного выше отказа по памяти не произойдет:

       BEGIN NEW(T1);...NEW(T2);...NEW(M1);...

             DISPOSE(T1);...DISPOSE(T2);... NEW(M2);...

Здесь при обработке запроса NEW(M2) в пуле динамической па­мя­ти будет находиться один свободный фрагмент объема шесть слов, об­ра­зо­­ван­ный "склеиванием" элементов Т1^ и T2^, выполненным при об­ра­ботке зап­роса DISPOSE(T2). В общем случае вопросы эффективной ре­ализации управления динамической памятью, обеспечивающей ми­ни­мум отказов при ограниченном объеме, составляют отдельную проб­ле­му. Здесь мы только заметим, что с организацией выделения "пер­вого подходящего" фрагмента памяти в программировании свя­зы­ва­ют такие термины как "хип" или "куча", относящиеся скорее к про­фессиональному жаргону, чем к научно-методической тер­ми­но­ло­гии. Тем не менее эти термины до­вольно образно характеризуют прин­ципы организации динамической памяти.

Организация корректной последовательности запросов связана, кро­ме того, как минимум еще с двумя проблемами. На том же жар­го­не их называют проблемы "висячих ссылок" и "мусора", а оп­ре­де­ля­ют они две стороны одной и той же ошибки, заключающейся в некор­ре­кт­ной работе с указателями. Следующий фрагмент программы ил­лю­с­т­ри­рует возникновение таких ошибок (тип "Треугольник" описан выше).

        VAR T1, T2:Треугольник;

        BEGIN NEW(T1);...T2:=T1;...

              DISPOSE(T1);         (* T2-"висячая ссылка" *)

              ............

              NEW(T1);...NEW(T2);...

              T1:=T2;              (* Остался "мусор" *)

Из этого примера понятно, что "висячая ссылка" - это ука­за­тель при­кладной программы, указывающий на свободный фрагмент ди­на­ми­чес­кой памяти. Поскольку этот фрагмент может быть выделен сис­темой по какому-либо запросу другой прикладной программе, Т2 мо­жет открыть дос­туп к  "чужим" данным и "разрешить" их ин­тер­пре­тацию как тре­у­голь­ника. Последствия такой интерпретации в об­щем случае непред­ска­зуемы. Заметим, что "висячая" ссылка и "пус­тая" ссылка (имеющая значение NIL, см. pазд.III) являются со­вер­шен­но разными поня­ти­я­ми. "Мусор" - это занятый фрагмент дина­ми­чес­кой памяти, к которому в прикладной программе потерян доступ. В приведенном примере мусором оказался старый треугольник Т1^, на который указывал Т1 до пе­ре­д­виж­ки (установки на Т2). Этот мусор неустраним: программист не име­ет к нему доступа, а система управления "считает" мусор занятым фраг­ментом памяти.

Объединяет эти два вида ошибок одно общее обстоятельство: они не обнаруживаются исполнительной средой. Идентифицировать по­доб­ные ошибки можно только путем тщательной проверки и отладки прог­раммы. И, наконец, по своим возможным влияниям на работу прог­раммы мусор го­раздо "безобиднее" висячей ссылки. Он фак­ти­чес­ки приводит только к увеличенному расходу памяти, в то время как висячая ссылка спо­соб­на при определенных условиях полностью па­рализовать процесс вы­пол­нения программы. В сложных системах "це­на" висячей ссылки может оказаться очень высокой.

Использование автоматической памяти связано с соз­да­ни­ем / унич­то­жением специальных элементов хранения, связанных с актив­ны­ми объ­ектами - действиями или процедурами. Любая процедура тpе­бует для выполнения собственной индивидуальной локальной сре­ды. Подобную сре­ду образуют локальные переменные, объявленные в про­цедуре, фор­маль­ные параметры, элемент хранения адреса воз­вра­та в процедуру, т.е. набор объектов, обеспечивающих выполнение дей­ствий, связанных с процедурой. Необходимость в локальной сре­де возникает только в мо­мент вызова процедуры - момент интер­пре­та­ции объекта процедурного типа. После завершения такой интер­пре­тации необходимость в локальной сре­де исчезает. Таким обра­зом, время жизни локальной среды ог­ра­ни­чи­вается временем от­ра­бот­ки программы, в которой она описана. Со­от­ветственно запрос на создание локальной среды связан с вызовом про­цедуры, а запрос на уничтожение - с окончанием фазы активности объекта (оператор RETURN или END в теле процедуры). Например:

           VAR W1, W2: PROC;

           PROCEDURE Работа_1;

                VAR A: INTEGER;... BEGIN... W2;...

           END Работа_1;

           PROCEDURE Работа_2;

                VAR A: INTEGER;... BEGIN... W2;...

           END Работа_2;

           BEGIN... W1:=Работа_1;... W2:=Работа_2;... W1;...

В этом фрагменте описаны два активных объекта процедурного типа PROC = PROCEDURE(): W1 и W2 и две процедуры без параметров: Работа_1 и Работа_2, которые могут использоваться как константы ти­па PROC. Интерпретация (активизация) W1 приведет к вызову Работы_1 и созданию локальной среды (содержащей переменную А). В процессе выполнения Работы_1 производится активизация объекта W2 и соответственно  создание локальной среды для Работы_2. В любой те­кущий момент времени в системе могут быть активны несколько объ­ек­тов. (В этом примере активизация W1 приводит к активизации W2, за­тем они оба остаются в активном состоянии и затем теряют свою активность в обратной последовательности: сначала  пас­си­ви­руется W2, затем W1). Последовательность активации и пассивации свя­зана с вло­женностью вызовов процедур, соответственно уп­рав­ле­ние авто­ма­ти­чес­кой памятью основывается на использовании стека - структуры, под­­держивающей такую вложенность. Ниже для этого фраг­мента при­ве­де­­на иллюстрация распределения автоматической па­мя­ти, суще­ствую­ще­го в течение совместной активности объектов W1 и W2.

                ┌─   ┌───────────────────┐   ─┐

                │    │ Переменная А      │    │

                │    ├───────────────────┤     > Локальная среда

                │    │ Адрес возврата    │    │       для W1

Занятое прост-  <    ├───────────────────┤   ─┤

  ранство       │    │ Переменная А      │    │

                │    ├───────────────────┤     > Локальная среда

                │    │ Адрес возврата    │    │       для W2

   Вершина  ──> └─   │───────────────────┤   ─┤

    стека            │                   │    │

автоматической       │                   │    │

    памяти           │                   │    │   Свободное

                     │                   │     > пространство

      Пассивация     │                   │    │     памяти

    │      ^         │                   │    │     

    │      │         │                   │    │     

    │      │         │                   │    │     

    v      │         └───────────────────┘   ─┘     

Активация      

При активации каждого нового объекта вершина стека "опус­ка­ет­ся вниз" на величину, определяемую размерами локальной среды этого объ­екта,- при его пассивации вершина стека "поднимается вверх". С использованием автоматической памяти связаны две ос­нов­ные про­б­лемы: рекурсии и множественности ассоциаций.

Рекурсия - механизм, позволяющий объекту совершать само­ак­ти­ва­ц-ию. Например, по схеме:

               W1-->W1 (прямая рекурсия)

        или    W1-->W2 ...-->W1 (косвенная рекурсия).

Прямая рекурсия связана с непосредственной повторной (вло­жен­ной) активацией, а косвенная - с опосредованной (причем число пос­­ред­ников в схеме W1-->...-->W1 может быть произвольным). Ис­поль­зо­ва­ние рекурсии напрямую связано с размерами рабочего прост­ранства авто­матической памяти. Использование рекурсивных акти­ваций объ­ек­тов, с одной стороны, позволяет иметь очень лако­нич­ные и емкие по со­держанию программы, с другой стороны, в ре­кур­сивных схемах (особенно в косвенной рекурсии) возрастает ве­ро­ятность появления трудно идентифицируемых ошибок.

Множественность ассоциаций заключается в том, что в классе ав­­то­матической памяти могут быть одновременно размещены нес­коль­ко од­ноименных объектов, имеющих в общем случае различные значе­ния и относящиеся к разным активностям одного и того же или опять-таки разных объектов. В приведенном примере существуют два одно­именных объекта: переменная А, связанная (ассоциированная) с активностью W1, и переменная А, ассоциированная с активностью объ­екта W2. В со­­ответствии с принципом стека система упpавления автоматической па­мятью всегда pассматpивает в качестве активной пос­леднюю соз­дан­ную ассоциацию (самую "ближнюю" к вершине стека авто­матической па­мя­ти). Возникновение множественности ассоциаций обусловлено только использованием в прикладных программах одно­и­мен­ных переменных с различной областью действия (областью ви­ди­мос­ти). Если уж ис­поль­зо­вание таких переменных и является необ­хо­димым (в чем всегда сто­ит усомниться), то при их интерпретации следует помнить о мно­же­ст­вен­ности ассоциаций.


▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄А___Б___v_Г___ Д___q_U___ Г___m_╜___ ╛___h_╤___ ¤___e_└___ ╟___b_е__ ┬________ ____Z___________________@___ ____@____@_________ -___x_о___ ├___s_ч___ ■___n_Ф___ й___i_з­__ │­__d_└­__ ╨­____w"__ Ы"__Z_B#__ __________________________________B#__C#__t_j#__ k#__m_r#__ s#__f_а#__ б#____Ў#__ ў#__X_∙#__ ·#__Q_Н%__ w"__ Ы"_______Ї______Ї______Ї______Ї______Ї______Ї
Н%__О%__t_с&__ т&__m_='__ >'__f_P'__ Q'____0(__ 1(__X_3(__ 4(__Q_q(__ w"__ Ы"_______Ї______Ї______Ї______Ї______Ї______Ї
q(__(__v_к(__ ┤(__q_╗*__ ╩*__l_/+__ N+__g_S+__ T+__`_я+__ _,__[__,__ 5,__V__0__ __________________Ї____________________
_0__*0__v_51__ X1__q_Q9__ b9__n__;__ у;__k_O<__ T<__f_У<__ Я<__a_Y=__ v=__\_OA__ _______________________________________
OA___A__x_yA__ ИA__u_▒B__ ╛B__p_ЕC__ ЩC__k_▀C__ ьC__f_TD__ eD__a_УD__ жD__\_3J__ _______________________________________
3J__CJ__v_~J__ ЮJ__q_WK__ sK__l_ёL__ ўL__g_VN__ gN__b_hN__ iN____"O__ 2O__Z_+T__ _______________________________________
+T__;T__v_<T__q_╥U__ ╘U__l_┘X__ сX__g_╢\__ ╧\__b_Va__ ka__]_Ъb__ Ыb__V_3c__ +T__ ________
______________________________3c__5c__t_Уc__ Фc__m__f__ _f__f__g__ _g__a_▀g__ щg__^_ak__ ik__Y_jk__V_╪k__ +T__ ________________________
______
______
╪k__┘k__t_ l__ #l__o_/m__ 0m__h_╓p__ ·p__c_]q__ nq__a_Чq__ │q____├q__ Zr__]_░t__ Вu__Z_____7_7_7___________
___________
_Вu__pv__ zv__v_
w__ 1w__q_╫x__ яx__l_нy__ ├y__g_|z__ Иz__d_┼{__ ▄{__a_F~__ b~__^_BД__ __________________________________BД__DД__y_dД__ eД__w_ЭД__ ЯД__u_┴Д__ ├Д__s_уД__ фД__q__Е__ _Е__o_>Е__ @Е__m_`Е__ aЕ__k_ПЕ__ СЕ__i_____7_7_7_7_7_7_7_7_7_СЕ__ШЕ__ нЕ__y_├Е__ ┼Е__w_хЕ__ цЕ__u__Ж__ _Ж__s_ЭЗ__ ║З__n__Й__ ­Й__i_zЙ__ МЙ__d_YЛ__ ПЕ__ СЕ__________________7_7_7_7_YЛ__jЛ__v_ Л__ _М__q_┘М__ уМ__l_шМ__ яМ__g_шН__ ЄН__b_UО__ aО__]__Т__ _Т__V__Т__ YЛ______
______________________________
_Т___Т__t_эТ__r_мЧ__ ╦Ч__p_╪Ч__ ┘Ч__n_єЧ__ √Ч__l__Ш__ 4Ш__j_6Ш__ `Ш__h_bШ__ eШ__f_gШ__ КШ__d______7_7_7_7_7_7_7________
_КШ__МШ__ еШ__y_жШ__ оШ__w_╗Ш__ ┌Ш__u_█Ш__ ▀Ш__s_уШ__ _Щ__q_#Щ__ MЩ__o_OЩ__ RЩ__m__Щ__ ЙЩ__k_НЩ__ _7_7__7_7_7_7_7_7_7_7_НЩ__╖Щ__y_╣Щ__ ╝Щ__w_═Щ__ ўЩ__u__Ъ__ 2Ъ__s_4Ъ__ ^Ъ__q_█Ы__ эЫ__l_╫Э__ 'Ю__j_-Ю__ .Ю__h_6Ю__ 7Ю__f__7_7_7______7_7_7_7_7_7Ю__HЮ__ IЮ__y_RЮ__ SЮ__w_]Ю__ ^Ю__u_цЮ__ чЮ__s_ыЮ__ ЇЮ__q_¤Ю__ Я__o__Я__ _Я__m__Я__ XЯ__k_ZЯ__ _7_7__7_7_7_7_7_7_7_7_ZЯ__ХЯ__y_ЧЯ__ ╥Я__w_╘Я__ _а__u__а__ Hа__s_Jа__ Ба__q_ъл__ _м__l_
▒__ ╧▒__i_b┤__ n┤__f_S╢__ ZЯ__ ____________7_7_7_7_7_S╢___╢__v_"╣__ A╣__q_t╣__ П╣__l_p╝__ ╝__g_у┼__ х┼__`_╞__ _╞__Y__╞__ _╞__R_b┤________
______
______
_____________________╞___╞__ _╞__t_У╨__ ж╨__o_Я╪__ ╗╪__j_u▌__ Л▌__e_╬▌__ _▐__`_Y▀__ }▀__[_ф__ "ф__X_______________________________________

"ф__Aх__ [х__x_Qц__ mц__s_нц__ ┬ц__n_╪ш__ тш__i_шш__ Єш__d_#щ__ :щ____Bщ__ Pщ__Z__ы__ ___________________________________ы__&ы__v_Оы__ еы__q_бь__ вь__o_▒ь__ █ь__m_      э__ Pё__k_~ё__f_zў__d_вў____\___]__     __ _ы__ ____7_6____7_6____7_7_7__________
_     __­    __v_\

__ c

__s_F___ 
___p_K___ W___m_Z___ f___j_╠___ ┌___g_+___ C___b_\___ s___]___________________________________s___ъ___ ____v_В___ М___q_└___ с___l_=___ h___g_▀___ !‑__b_k‑__ Ж‑__]_а#__ ╢#__Z_ь(__ __________________________________ь(__є(__v_5,__ N,__t_P,__ ?0__r_A0__ у1__p_ц1__k__@__i_#@__d_╦B__b_┌B__]_^G__[_sG__V_ь(_6____7_6____7_6____7______7_7_7_____
sG__НG__y_ЧG__t_╙G__ 

H__o_║M__m_▀M__h_сM__f_уM__a_ыM__^_?N__ ╪P__\_ЄP__Y_СQ__ _S__W_ь(__7____7____6____7_6____7_6____6____7
_S__:S__v__T__t__T__o_)T__ █T__m_▄T__ сM__f_уM__a_ыM__^_?N__ ╪P__\_ЄP__Y_СQ__ _S__W_ь(__7____7____6____7_6____7_6____7_6____А___Г___i_Ж___W_╔___J_с___J_у___H_______________________________╪P__\_ЄP__Y__C
B_!_____7_р__< ‑_______Ё_______<"‑_______Ё_______у_______n_:___n_<___l_>___l_L_____U___]_Г___[____________________________________________р__< ‑_?_C
B_!_____7_р__C
B_!_____7_р__Г___Е___n_Ц___k_Ш___i_Ъ___i_Ь___i_Ю___i_а___i_в___i_________________________________________________B_!_____7_C_<_
B_!_____7_р__в___д___y_ж___y_и___y_к___y_м___y_о___y_╝___l_╜___ ________________________________________________B_!_____7_C_
B_!_____7_р__C_╜___└___i_╧___g_╤___e_U___c_j___c_Ц___c_ ___c_Т___c_________________________________________________B_!_I_C_I_<!‑_______Ё_______Т___к___y_ш___y_%___y_3___w_*

__u_E__u_.___u_╥___u_═___u_______________________________________________________‑_______Ё_C_?_I    ═___М___y_╬___w_O___u_п___u_Б___u_7"__c_F$__a_з%__a___________________________________________________C_C ‑_______Ё_______C_?_C_з%__╫%__y__&__y_T&__y_f&__y_Я&__y_▐&__y_р&__y__(__y_∙*__y_ -__y_____________________________________________________________?_C

      -__?-__y_╢.__y_ф0__y_Н5__y_к9__y_▀:__y__;__w_у;__u_х;__u_________________________________________________________________C_?_C    х;___?__y_щ@__y_&A__y_УA__y_·B__y_лD__y_УG__y_▓H__y_хH__y__I__y_____________________________________________________________?_C

_I__      I__y__N__y_ЇS__y_╘U__y_·U__y_+V__y_[V__y_wV__y_+W__y_TW__y_____________________________________________________________?_C

TW__
X__y_+X__y_эZ__y_X[__y_[__y_╗[__y_т[__y_·[__y_,\__y_╫]__y_____________________________________________________________?_C

╫]__  ^__y_=^__y_?^__y_&___y_Q___y_S___y__a__y_7d__y_mg__y_Bh__y_____________________________________________________________?_C

Bh__sh__y_uh__y_Hi__y_k__y_кk__y_мk__y_1l__y_Nl__y_}l__y_┤l__y_____________________________________________________________?_C

┤l__ъl__y_лm__y_╥m__y_╧n__y_·n__y_Зo__y_╕o__y_Gp__y_[q__y___________________________________________________________________?_C      [q__]q__y_Йq__y_├q__y_ q__y_+r__y_Xr__y_█s__w_Уt__w_░t__u_________________________________________________________________?_C_G  ░t__Вu__y_Дu__y_­v__y_Jw__y_qw__y_Єy__y_╨z__y_Їz__y_({__y_A{__y_____________________________________________________________C_C

A{__e__y_Ь__y_d~__y_Я~__y_б~__y_┼__y_____________________________________________________________C_C

┼__tА__y_А__y_╖А__y_┼А__y_№А__y__Б__y_#Б__y_)Б__y_хБ__y_уГ__y_____________________________________________________________C_C

уГ__хГ__y_
Д__w__Д__w_,Д__w_.Д__w_PД__w_ЙД__w_лД__w_нД__w___________________________________________________________________G_C      нД__╧Д__y__Е__y_(Е__y_*Е__y_LЕ__y_{Е__y_ШЕ__y_пЕ__y_╤Е__y__Ж__y_____________________________________________________________G_G

_Ж___Ж__y_­Ж__y_еИ__w_ўК__w_ Л__w_{О__w_ХП__w_уП__w__Р__w___________________________________________________________________C_G      _Р___Р__y_┘Р__y_
С__y_5С__y_lС__y_эТ__y_+У__y_dУ__y_ИУ__y_┤У__y_____________________________________________________________C_C

┤У__╞У__y_шФ__w_2Ч__u_4Ч__u_oЧ__s_qЧ__s_бЧ__s_═Ч__s__Ш__s_______________________________________________________________G_C_I_C      _Ш__6Ш__y_gШ__y_МШ__y_░Ш__y_уШ__y_#Щ__y__Щ__y_НЩ__y_═Щ__y__Ъ__y_____________________________________________________________I_G

_Ъ__4Ъ__y_`Ъ__y_bЪ__y_vЫ__w_ЫЬ__w_┐Ь__w_ьЬ__w_*Э__w_LЭ__w___________________________________________________________________C_G      LЭ__╒Э__y_╫Э__y_'Ю__w_рЮ__w__Я__w_ZЯ__w_ЧЯ__w_╘Я__w__а__w___________________________________________________________________G_C      _а__Jа__y_Га__y_Еа__y_╖а__w_╣а__w_Kв__w_7д__w_tд__w_░д__w___________________________________________________________________C_G      ░д__щд__y_е__y_ле__y_█е__y_ж__y_<з__y_Bи__y_Уи__y_си__y_%й__y_____________________________________________________________C_C

%й__[й__y_│й__w_єй__u_bм__s_dм__q_Жм__q_╜м__q_╒м__q__н__q_____________________________________________________________C_I_C_I_C      _н__Hн__y_|н__y_Йн__y_Лн__y_&п__y_Є░__y_
▒__w_╧▒__u_╥▒__u_____________________________________________________________C_I_C_?_C      ╥▒__є▓__y__┤__y_9╢__y_P╕__y_Т╣__y__╗__y_в╝__y_╪╝__y_5╜__y_~┴__y_____________________________________________________________?_C

~┴__Ж├__y_И├__y_н├__y_р├__y_ў├__y_z╟__y_P╩__y_┤╩__y_й╦__y_▄╦__y_____________________________________________________________?_C

▄╦___╠__y_\╠__y_Ф╠__y_╠╠__y__═__y_<═__y_1╬__y_ю╬__y_Є╧__y_в╤__y_____________________________________________________________?_C

в╤__┤╤__y_╤╤__y_▐╤__y__╥__y_%╘__y_S╘__y_v╘__y_П╘__y_н╘__y_╦╘__y_____________________________________________________________?_C

╦╘__∙╘__y__╒__y__╒__y_,╒__y_F╒__y_d╒__y_В╒__y_▓╒__y_╛╒__y_└╒__y_____________________________________________________________?_C

└╒__п╓__y_▒╓__y_╩╓__y_ф╓__y__╫__y_'╫__y_d╫__y_М╫__y_Ы╫__y_и╫__y_____________________________________________________________?_C

и╫__╛╪__y_-┘__y_e┘__y_╓┘__w__┌__u_#▌__s_6▐__q_O▐__q_|▐__q_____________________________________________________________C_I_C_I_C      |▐__Я▐__y_▄▐__y__▀__y__▀__y_ ▀__y_╒▀__y_
р__y_Oу__y_hф__y_нц__y_____________________________________________________________I_C

нц__╪ч__y_2ъ__y_hъ__y_Ўы__y_°ы__y_Vь__w_Бь__w_дь__w_
э__w___________________________________________________________________G_C 
э__Dэ__y_Вэ__y_┴э__y__ю__y_?ю__y_~ю__y_╟ю__y_тю__y_ью__y_Ўю__y_____________________________________________________________G_G

Ўю___я__y_

я__y__я__y__я__w_╫я__w_∙я__w_+Ё__w_PЁ__w_∙Ё__w___________________________________________________________________C_G      ∙Ё__{Є__y_}Є__y_Є__y_БЄ__y__є__y_7є__y_\є__y_~є__y_Ює__y_ї__y_____________________________________________________________C_C

ї__@ї__y_╡ї__y__ў__y_ў°__y__∙__y_.∙__y_[∙__y_Б∙__y_д∙__y_╟∙__y_____________________________________________________________C_C

╟∙__у∙__y_√∙__y_0·__y_e·__y_Ъ·__y_╧·__y_╤■__y_Ї___y_2___y_Z___y_____________________________________________________________C_C

Z___Й___y_о___y_░___y_^___y_Ъ___y_┘___y_┤

__y_+
__y_Z
__y_└
__y_____________________________________________________________C_C


__Ё
__y_#
__y_)__y_U__y_n__y_У__y_╦__y_"___y_F___w___________________________________________________________________?_C    F___
___y_
___y_ы___y_╡___y_р___y_____y_╛___w_╞___u_$‑__u_________________________________________________________________C_I_C      $‑__ф‑__y_&­__y_d­__y_c __y_f"__y_с)__y_у)__y__*__y_3*__y_o*__y_____________________________________________________________I_C

o*__В*__y_╖*__y_╪*__y__+__y_U+__y_i+__y_д+__y_┘+__y_$,__y___________________________________________________________________I_C      $,__&,__y_(,__y_*,__y_P,__y_v,__y_Ы,__y_▄,__y__-__y_R-__y_y-__y_____________________________________________________________I_G

y-__а-__y_╟-__y_ю-__y_).__y_b.__y_Й.__y_░.__y_╫.__y_№.__y_!/__y_____________________________________________________________I_G

!/__c/__y_г/__y_▄/__y__0__y__0__w_Й1__w_x3__w_и3__w_с3__w___________________________________________________________________C_G      с3__у3__y_╓6__y_c8__y_e8__y_Ж8__y_л8__y_щ8__y__9__y_+9__y_e9__y_____________________________________________________________C_C

e9__g9__y_ю<__y_#?__y__C__y_8C__y_XC__y_МC__y_жC__y_╞C__y_·C__y_____________________________________________________________C_C

·C___D__y_RD__y_TD__y_кH__y_мH__y_▌H__w_
I__w_QI__w_ПI__w___________________________________________________________________G_C      ПI__└I__y_ёI__y_4J__y_rJ__y_гJ__y_╘J__y__K__y_BK__y_ВK__y_┐K__y_____________________________________________________________G_G

┐K__ЎK__y_-L__y_dL__y_ЫL__y_нL__y_уM__w_?N__w_iN__w_аN__w___________________________________________________________________C_G      аN__вN__y_╪P__y_█T__y_▄T__ ▌T__ __________________________________________________________________________________________C_C__╥_═;L, __C6К_Л$____▒:_╥_═;L, __C6К_Л$____╨7▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄7_

_____=___ж___C__
___п___ '__└-__▓4__
;___A__╔G___N__∙T__ Z__Р`__jg__jl__╪r__╡x__­~__ОГ___К__YР__8Ц__╠Ь__0д__эй__еп__W╡__╙╗__@┬__╞╚__r╧__З╘__х╪__а▐__#х__vы___Є__w°___■__0___@
__в___ш___д___5$__X*__┐1__х7__+>__╘C__-L__ЩR__\T______________ _______ ____k___ ____B___ ____R___ ____u___ ____v___ ____[___ __      _U___ __

_*___ __
_i___ __
_e___ ___[___ __
_<___ ____ъ___ ____X___ ____A___ ____Г___ ____╜___ ________ ________ ____]___ ____2___ ____z___ ____
___ ____9___ ____ї___ ____═___ ____b___ __‑_O___ __­_╞___ __ _

___ __!_░___ __"_%___ __#_q___ __$_╡___ __%_

___ __&_____ __'_Б___ __(_

___ __)_P___ __*_о___ __+_0___ __,_н___ __-_M___ __._└___ __/_,___ __0_?___ __1_9___ __2_!___ __3_x___ __4_____ __5_6___ __6_├___ __7___▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄________________=_______\T____________________╢_▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄____=______Б__\T_____Б__]T__   ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄______________ _(_______01/01/9412/03/93\T__▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄


© 2012 Рефераты, курсовые и дипломные работы.