TURBO PASCAL |
Новости
|
ООП - это очень простоВыпуск № 6 Выдавать глобальные идеи - это удовольствие. Модуль uRec.pasЗдесь описывается хранимая единица TOne. Для удобства редактирования информации она "выведена" в отдельную запись TInfo. Обратите внимание, что для каждой строки обязательно нужно задать ее длину. Иначе длина строки будет равна 255 символам, а где Вы видели такую фамилию? То есть, если не задать длину строк, то только 5-10 % базы данных будет содержать что-то полезное. Длина строчек задается константами, что облегчает изменение. Здесь же определена еще одна константа с идентификатором версии, но не программы, а структуры полей. Она нужна вот для чего: в дальнейшем я собираюсь хранить информацию в файле. Доступ к этому файлу собираюсь организовать самый быстрый (но пока без потоков, через типизированный файл), а для этого нужно, чтобы каждая единица хранимой информации имела фиксированную длину. Теперь, допустим, сохранили файл MyDB.db при одной структуре записи. Через неделю решили, что надо ввести поле с номером телефона, или просто изменили длину поля для хранения фамилии. Вы легко изменяете структуру записи TInfo. (Я позднее покажу "как", если сами не сообразите) и открываете файл. Длина одной единицы информации изменилась. Теперь, если будете читать "старый" файл, сохраненный по старым стандартам, то получите белиберду. Будет удобно, если программа предупредит Вас о несовпадении версий и предложит сначала запустить другую программу для конвертирования ее структуры. То есть, эта программа прочитает файл по старому стандарту, и сохранит по новому. И вот для реализации этого первая запись в файле содержит номер варианта. При открытии файла нужно будет только сравнить первые символы в файле к константой. Да! И внеся изменения - измените константу с номером версии! Теперь немного слов о том, как будем хранить. Данные я буду располагать в динамической памяти компьютера. И помещать их туда буду оператором New. Обращение к таким данным происходит по их адресу. В "объекте-хранителе" (TBaseObj) я буду хранить адрес первой структуры. В поле Next буду записывать адрес следующей такой структуры или константу NIL, если эта структура последняя и следующей нет. Получается цепочка, по которой можно "проходить" только в одном направлении от начала к концу. Отсюда и название "односвязный список". О таких списках написано уже много и в учебниках и в статьях. (Если Вы не разберетесь в них, напишите - поясню дполнительно) В модуле uRec.pas описаны еще две подпрограммы, которые облегчат будущую работу.
В этом же модуле находится и "хитрая" функция HeapFunc. Дело в том, что Паскаль, если не хватает места для размещения чего-либо в динамической памяти компьютера, реагирует на это "жестко" - аварийно завершает работу программы! Это "не есть удобно" - лучше бы дать "знать" о проблеме программе, а она (мы так должны написать ее, программу эту самую!) пусть посмотрит, может что-то уже можно "выгрузить" из динамической памяти или, по крайней мере, пусть корректно завершит работу. Разработчики заложили в компилятор Borland Pascal такую возможность. У "них"есть функция HeapFunc, которая вызывается, когда есть проблемы с памятью. Программа "смотрит", что возвращает эта функция. Если 0 (ноль), то аварийно завершается. Если функция вернет 2 - это значит, что все прошло хорошо (!?). Неудача была вызвана временными причинами и нужно сделать вторую попытку. Если возвращается значение 1, то это сигнал о том, что памяти нет, но и работу программы завершать не надо. В указатель переменной в этом случае записывается значение NIL. Вот мы и делаем "насильственное" действие. Разработчиками предусмотрено, что программа "узнает" о нахождении такой "Хиповой" функции из переменной процедурного типа HeapError. В исполняемом блоке модуля в эту переменную записывается адрес "нашей" функции. (Видите, все продуманно в Паскале!) unit uRec; interface CONST cNameLen = 12 ; cForeNameLen = 12; cVersion : String[cNameLen] = 'Ver.1.0 21.11.03'; TYPE {Содержательная часть хранимой единицы} TInfo = record Name : String[cNameLen]; ForeName: String[cForeNameLen]; Age : Byte; end; {Хранимая единица} POne = ^TOne; TOne = record Info : TInfo; Next : POne; end; function NewOne(AName: String; AForeName: String; AAge: Byte): POne; procedure PrintFullInfo(AOne: Pone); implementation {Такая функция облегчит работу} function NewOne(AName: String; AForeName: String; AAge: Byte): POne; var p: POne; begin New(p); with p^ do begin Next := nil; Info.Name :=Copy(AName , 1, cNameLen); Info.ForeName:=Copy(AForeName, 1, cForeNameLen); Info.Age:=AAge end; NewOne:=p end; procedure PrintFullInfo(AOne: POne); begin if AOne = nil then Exit; With AOne^.Info do WriteLn('Фамилия: ',Name,'. Имя: ', ForeName,'. Возраст: ',Age); end; {Эта функция нужна для того, чтобы при возникновении ошибок программа не завершалась аварийно} function HeapFunc(Size: Word): Integer; far; begin HeapFunc:=1 end; BEGIN {Настройка функции} HeapError:=@HeapFunc; END. Если что-то осталось непонятным, то пишите - поясню BaseObj.pas
Основную работу совершает объект, который описан в модуле, текст которого показан ниже. В разделе
TYPE описывается только структура объекта. Перед этим (это исключение из общего правила для Паскаля), определяется
указатель на объект
unit BaseObj; interface uses uRec; TYPE PBaseObj = ^TBaseObj; TBaseObj = object constructor Init; destructor Done; virtual; procedure Add(ANewOne: POne); virtual; procedure PrintAll; virtual; private Node: POne; end; implementation constructor TBaseObj.Init; begin {Первая запись всегда - информация о версии} Node:=NewOne(cVersion, cVersion, 0); end; procedure TBaseObj.Add(ANewOne: POne); var p: POne; begin if ANewOne = nil then Exit; {Находим последнюю запись} p:=Node; while p^.Next <> nil do p:=p^.Next; p^.Next:=ANewOne; p^.Next^.Next := nil end; procedure TBaseObj.PrintAll; var p: POne; begin p:=Node; while p <> nil do begin if Copy(p^.Info.Name,1,3) <> 'Ver' then PrintFullInfo(p); p:=p^.Next end end; destructor TBaseObj.Done; var p: POne; begin While Node <> nil do begin p:=Node^.Next; Dispose(Node); Node:=p end end; BEGIN END. Описание методов объекта:
Если не ясно что осталось, то напишите. А я только заканчиваю описание главной тестирующей программы и отсылаю. И так уже затянул бессовестно
Test.pasЭто и есть "программа". Она только вызывает объект, кое-что в него заносит, кое-что выводит. В общем, рутинная работа ... program Test; uses BaseObj, uRec; var BO: PBaseObj; BEGIN Bo:=New(PBaseObj, Init); if BO <> nil then with BO^ do begin Add(NewOne('Surin','Борис',12)); PrintAll; Dispose(BO, Done); end; END.Здесь важно заметить, как объектом пользуются. То, что было описано в других модулях ( в разделе TYPE интерфейсной части модуля, и реализовано в разделе реализаций) - все было фантомом (объекты и есть объекты!). Они не способны работать, так как их нет в памяти компьютера. Для того, чтобы занести, нужно создать экземпляр объекта с помощью таких действий:
Рассылку поддерживает сайт www.turbopascal.tk При перепечатке ссылка на сайт обязательна |
(с)Все права защищены По всем интересующим вопросам прошу писать на электронный адрес |