3 Подпрограммы в модулях
Процедуры и функции могут использоваться в модулях наравне с другими Pascal-объектами. Однако для них имеются особенности, обусловленные их структурой. Заголовок подпрограммы содержит всю информацию, необходимую для ее вызова: ее имя, количество и типы параметров и (для функций) тип результата. С другой стороны, описание подпрограммы содержит блок, раскрывающий ее алгоритм. Можно считать, что заголовок подпрограммы является ее интерфейсом, а описание и операторы – ее реализацией. Поэтому логично в интерфейсной части модуля представить только ЗАГОЛОВКИ процедур и функций, они будут видимы (доступны) для других программ, а их полные описания будут содержаться в разделе реализации. При этом полное описание подпрограммы может иметь СОКРАЩЕННЫЙ заголовок, состоящий только из служебного слова procedure или function, имени подпрограммы и символа ';'. (Можно повторить полный заголовок подпрограммы, но тогда он должен быть совпадать с заголовком в интерфейсной части).
В качестве примера можно привести модуль, содержащий средства работы с комплексными числами:
unit CmplVals;
interface
type
Complex = record { способ представления комплексных чисел }
Re, Im : real
end;
{ Заголовки процедур, реализующих операции над комплексными числами }
procedure InitC (С:Complex;R,I:real);
procedure AddC (CI,C2:Complex;var R:Complex);
procedure MultC (CI,C2:Complex;var R:Complex);
procedure DivC (CI,C2:Complex;var R:Complex);
procedure WriteC(C:Complex);
implementation
{ Полные описания процедур (с сокращенными заголовками) }
procedure InitC;
begin
with С do
begin
Re:=R; Im:=I
end
end;
procedure AddC;
begin
with R do
begin
Re := Cl.Re+C2.Re; Im := Cl.Im+C2.Im
end
end;
procedure MultС;
begin
with R do
begin
Re := Cl.Re*C2.Re+Cl.Im+C2.Im; Im := Cl.Im*C2.Re+Cl.Re*C2.Im
end
end;
procedure DivC;
var
Tmp : real;
begin
with C2 do
Tmp:=Re*Re+Im*Im;
with R do
begin
Re := (Cl.Re*C2.Re+Cl.Im*C2.Im)/Tmp; Im := (C2.Re*Cl.Im+Cl.Re*C2.Im)/Tmp
end
end;
procedure WriteC;
begin
with С do
begin
Write(Re);
if lm=0 then
Exit;
if lm>0 then
Write('+');
Write(Im); Write('i')
end
end;
end.
Таким образом, механизм модулей позволяет скрыть детали реализации тех или иных программных подсистем, предоставив в распоряжение использующих их программ строго определенную совокупность интерфейсных объектов. Если необходимо, например, расширить модуль CmplVals введением новых процедур или изменить реализацию какой-либо процедуры, то если интерфейс модуля при этом останется неизменным, такая модификация НИКАК НЕ ОТРАЗИТСЯ на использующих программах.
4 Компиляция и использование модулей
Модуль компилируется точно таким же как и обычные программы. Можно компилировать из интегрированной среды или с помощью компилятора командной строки. Но так как модуль не является непосредственно выполняемой единицей, то в результате его компиляции образуется внешний файл с расширением .TPU (Turbo Pascal Unit). При этом имя файла до расширения .tpu совпадает с именем исходного файла (текста на паскале) до расширения .pas. Чтобы получить доступ к интерфейсным объектам модуля, необходимо в программе в предложении использования записать имя нужного TPU-файла. Общий вид предложения использования:
uses
U1, U2, U3;
где uses - служебное слово, U1, U2, U3 - идентификаторы используемых модулей. Это предложение должно быть записано сразу после заголовка программы. Если модуль использует объекты другого модуля, то такая спецификация должна следовать сразу после служебного слова interface.
При наличии предложения использования в данной программе считаются известными все описания из интерфейсной части подключенного модуля. К интерфейсным объектам модуля можно обращаться в программе точно так же, как если бы они были описаны в самой этой программе.
Следующий пример иллюстрирует использование модуля CmplVals:
program UsingComplex;
uses CmplVals;
var
C1, C2, C3 : Complex;
begin
InitC(1,2,C1); InitC(3,4,C2); MultC(C1,C2,C3); WriteC(C3);
DivC(C1,C2,C3); WriteC(C3)
end.
Необходимо особо отметить следующие важные моменты, связанные с использованием модулей:
1. Может случиться так, что некоторые идентификаторы интерфейсной части используемого модуля частично совпадают с идентификаторами использующей программы. В этом случае действует следующее правило видимости имен: интерфейсные идентификаторы модуля, указанного первым в uses-списке, образуют самый внешний блок программы; интерфейсные идентификаторы второго модуля образуют блок, вложенный в первый блок, и т.д. Если, например, в программе имеется спецификация вида uses А, В; то вложенность блоков выглядит так:
идентификаторы модуля А |
|||
|
идентификаторы модуля В |
||
|
идентификаторы программы |
||
|
|
||
|
|||
Таким образом, идентификаторы внешнего блока программы будут "экранировать" одноименные идентификаторы модулей А и В; аналогично, идентификаторы модуля А будут перекрыты одноименными идентификаторами модуля В.
Однако, существует возможность доступа к идентификаторам интерфейса используемого модуля несмотря на наличие в программе одноименных идентификаторов. Пусть имеется следующий модуль:
unit А;
interface
var
X : real; implementation
end.
Далее, пусть программа, использующая этот модуль, также содержит переменную X:
program Р; uses А;
var
X : integer;
begin
end.
Для того чтобы в программе Р иметь доступ к интерфейсной переменной X из модуля А, необходимо указать составное имя, структура которого похожа на селектор поля записи: А.Х
Здесь А - имя модуля, X - идентификатор его интерфейсной переменной. В этом случае конфликт имен снимается, так как простое указание имени X будет означать обращение к переменной из программы Р, например:
А.Х := Round(X);
2. Возможны случаи косвенных использований. Например, пусть имеются два модуля:
unit A; unit B;
interface interface . . . uses A;
end. . . .
end.
Если некоторая программа использует модуль В, то в соответствующей спецификации использования необходимо указать только модули, НЕПОСРЕДСТВЕННО используемые в программе. В данном примере достаточной является следующая спецификация:
program P;
uses В;
end.
(Необходимо отметить, что в фирменной документации по системе Turbo Pascal можно встретить и противоположное требование, согласно которому в спецификации использования должны быть указаны ВСЕ модули, прямо или косвенно используемые программой. Однако, из практики видно, что компилятор правильно обрабатывает программы, в спецификации использования которых указаны только модули, непосредственно используемые ею).
3. Схема использования модулей может образовывать древовидную структуру любой сложности, но при этом недопустимо явное или косвенное обращение модуля к самому себе. Так например, следующие отношения являются ошибочными:
unit A; unit В;
interface interface uses В; uses А;
end. end.
Однако допускается взаимное использования модулей, позволяющее ослабить указанное ограничение. В этом случае спецификация использования может указываться в разделе реализации. Такая возможность используется сравнительно редко поэтому мы ее рассматривать не будем.
4. Если в модуле имеется раздел инициализации, то операторы из этого раздела будут выполнены ДО начала выполнения программы, в которой используется данный модуль. Если программа использует несколько модулей, то их разделы инициализации будут выполнены в том же порядке, в котором эти модули перечислены в спецификации использования.
