На этом шаге мы рассмотрим структуру модуля.
Используя модули важно правильно указывать их имена. При подключении стандартных модулей достаточно корректно записать их идентификаторы в предложении uses.
При разработке собственных модулей необходимо помнить некоторые особенности:
Ниже показана общая структура модуля, дополненная комментариями, поясняющими смысл и назначение каждого раздела модуля.
unit <ИдентификаторМодуля>; {Интерфейсный раздел.} interface { В этом разделе описывается взаимодействие } { данного модуля с другими пользовательскими } { и стандартными модулями, а также с главной } { программой. Другими словами - взаимодей- } { ствие модуля с "внешним миром". } {Список импорта интерфейсного раздела. } uses { В этом списке через запятые перечисляются } { идентификаторы модулей, информация интер- } { фейсных частей которых должна быть дос- } { тупна в данном модуле. } { Здесь целесообразно описывать идентифика- } { торы только тех модулей, информация из } { которых используется в описаниях раздела } { interface данного модуля. } { Список экспорта интерфейсного раздела.} const { Список экспорта состоит из подраз- } type { делов описания констант, типов, } var { переменных, заголовков процедур и } procedure { функций, которые определены в дан- } function { ном модуле, но использовать кото- } { рые разрешено во всех других моду- } { лях и программах, включающих имя } { данного модуля в своей строке uses.} { Для процедур и функций здесь опи- } { сываются только заголовки, но с } { обязательным полным описанием фор- } { мальных параметров. } { Раздел реализации. } implementation { В этом разделе указывается реализацион- } { ная (личная) часть описаний данного моду- } { ля, которая недоступна для других модулей } { и программ. Другими словами - "внутренняя } { кухня" модуля. } { Список импорта раздела реализации. } uses { В этом списке через запятые перечисляются } { идентификаторы модулей, информация интер- } { фейсных частей которых должна быть дос- } { тупна в данном модуле. } { Здесь целесообразно описывать идентифика- } { торы всех необходимых модулей, информа- } { ция из которых не используется в описани- } { ях раздела Interface данного модуля и об } { использовании которых не должен знать ни } { один другой модуль. } { Подразделы внутренних для модуля описаний. } label { В этих подразделах описываются мет-} const { ки, константы, типы, переменные, } type { процедуры и функции, которые описы-} var { вают алгоритмические действия, вы- } procedure { полняемые данным модулем, и которые} function { являются "личной собственностью" } { исключительно только данного моду- } { ля. Эти описания недоступны ни од- } { ному другому модулю. Заголовки } { процедур и функций в этом подраз- } { деле допускается указывать без } { списка формальных параметров. Если } { заголовки указаны все же с пара- } { метрами, то список формальных па- } { раметров должен быть идентичен та- } { кому же списку для соответствующей } { процедуры (функции) в разделе } { interface. } {Раздел инициализации. } initialization { В этом разделе между зарезервированными словами } { initialization и finalization располагаются one- } { раторы начальных установок, необходимых для запуска } { корректной работы модуля. } { Операторы разделов инициализации модулей, используе-} { мых в программе, выполняются при начальном запуске } { программы в том же порядке, в каком идентификаторы } { модулей описаны в предложениях uses. Если операторы } { инициализации не требуются,то зарезервированное } { слово initialization может быть опущено. } finalization {Раздел завершения finalization является необя- } {зательным и может присутствовать только вместе с } {разделом инициализации initialization. В разделе } {завершения располагается список операторов, которые } {будут выполняться при завершении модуля, что обычно} {происходит при окончании работы приложения. Разделы} {finalization модулей приложения выполняются в } {порядке, противоположном выполнению разделов } {initialization этих модулей. Раздел завершения } {используется как правило для освобождения ресурсов, } {выделенных приложению в разделе инициализации и тем } {самым гарантирует корректное "чистое" завершение } {приложения. Особенно это важно в случаях, когда } {приложение заканчивается по возникновению } {исключительных ситуаций. } end.
Раздел finalization является нововведением языка Object Pascal, реализованной начиная с Delphi 2.0. Этот раздел отсутствовал как в Borland (Turbo) Pascal, так и 16-тиразрядной версии .
При работе с модулями необходимо помнить их основное отличие от процедур и функций.
Традиционные правила сферы действия глобальных и локальных переменных для модулей не работают. Языковая конструкция "модуль" была специально разработана, чтобы исключить влияние глобальных переменных, объявленных в главной программе, на внутренние описания модуля.
Поэтому, если все-таки возникает необходимость ввести какие-то доступные для всех блоков программы описания (то есть глобальные описания), то для модулей это можно сделать только одним способом: создать специальный модуль глобальных объявлений (в нижеприном примере - UGlob) и включить его в список импорта всех модулей, где требуются его описания.
Приведем простейший демонстрационный пример использования конструкции "модуль" для реализации работы со списковой структурой которая имеет стандартное исходное состояние из десяти элементов. В этом примере показана характерная структура взаимосвязей между модулями.
unit UGlob; { Модуль глобальных описаний } interface type TVal = Integer; { Чтобы не загромождать пример излишними деталями, для } { описания глобального типа TVal сделано простейшее } { переопределение типа. } implementation end.
unit UStack; { Библиотечный модуль, включающий сервисные } { процедуры и функции для работы со списковой } { структурой типа "стек". } interface { Список импортируемых модулей . } uses UGlob; { Достаточно указать только модуль } { UGlob, т.к. в описаниях раздела } { interface информация из других } { стандартных и пользовательских мо-} { дулей не используется. Модули, } { описанные в interface, будут дос- } { тупны также и в implementation. } { Список экспортируемых процедур. } procedure Push (Val : TVal) ; {Процедура занесения элемента в стек. } procedure Pop (var Val : TVal) ; {Процедура извлечения элемента из стека. } function GetStatus : string; {Функция, возвращающая строку состояния стека. } implementation {Список "личного" импорта модуля. } { uses . . . ; Подключение других модулей в разделе } { implementation для реализации UStack } { не требуется. Хотя информация из модуля } { UGlob используется в implementation, } { указывать его в этом списке недопустимо, т.к. } { описанный в нем тип TVal используется в заго- } { ловках процедур Push и Pop и каждый модуль может } { быть указан только один раз - или в interface, } { или в implementation. Модули, описываемые в } { разделе implementation, в принципе допускается } { подключать в разделе interface. Однако это } { считается плохим стилем написания модулей, } { т.к. информация из этих модулей в описаниях } { раздела interface не используется. } type TPtr = ^TElem; { Тип указателя на элемент стека. } TElem = record { Тип элемента стека. } Inf : TVal; Link : TPtr end; var Top : TPtr; { Указатель на верхушку стека. } Value : TVal; { Переменная для очистки стека } { в разделе finalization. } i : Byte; { Переменная для организации цикла. } { !!! Заголовки процедур и функций, которые уже } { !!! описаны в разделе interface, в разделе } { !!! реализации можно указывать без параметров.} procedure Push; var P : TPtr; begin New (P); P^.Inf := Val; P^.Link := Top; Top := P end; procedure Pop; var P : TPtr; begin if Top <> nil then begin Val := Top^.Inf; P := Top; Top := P^.Link; Dispose (P) end end; function GetStatus; var S, El : string; procedure PrintEl(P : TPtr); { Внутренняя для модуля процедура, выполняющая } { печать элементов стека. } { Заголовки процедур и функций, которые в разделе interface} { не экспортируются, в разделе implementation описываются } { всегда полностью (с параметрами) . } begin if P <> nil then begin PrintEl (P^.Link); Str(P^.Inf:3, El); S := S + El; end; end; begin S := ''; PrintEl(Top); GetStatus := S; end; initialization {Начальная установка верхушки стека. } Top := nil; {Создание исходной конфигурации стека из 10-ти элементов.} for i := 1 to 10 do Push(i); finalization {Завершающая очистка памяти, занимаемой стеком, } {независимо от того, в каком состоянии он остался } {после использования в других модулях. } while Top <> nil do Pop (Value); end.
unit UseStack; {Модуль UseStack использует стек, } {реализованный в модуле UStack. } interface uses {Информация из перечисленных стандартных модулей исполь- } {зуется для описания экспортируемых модулем UseStack } { формы Form1 и ее типа TForm1. Поэтому компилятор Delphi } {подключает их в разделе interface, а не в разделе } {implementation. } Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Label1: TLabel; Button1: TButton; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} uses UGlob, UStack; { Модули UGlob и UStack, описываемые в разделе } { implementation, в принципе можно было бы подключить и } { в разделе interface. Однако это считается плохим } { стилем написания модулей, т.к. информация из этих } { модулей в описаниях раздела interface не используется.} procedure TForm1.Button1Click(Sender: TObject); { По щелчку на кнопке Buttonl заносит } { случайное число в стек. } var Val : TVal; begin Val := Random(10); Push(Val); end; procedure TForm1.Button2Click(Sender: TObject); { По щелчку на кнопке Button2 удаляет } { верхний элемент стека. } var Val : TVal; begin Pop(Val) end; procedure TForm1.Button3Click(Sender: TObject); { По щелчку на кнопке Button3 выводит } { состояние стека в поле Edit1. } begin Edit1.Text := GetStatus; end; initialization { При запуске проекта инициализирует } { генератор случайных чисел. } Randomize; end.
Результат работы приложения показан на рисунке 1:
Рис.1. Результат работы приложения
Со следующего шага мы начнем знакомиться с динамически подключаемыми библиотеками.