Подключаемые модули - Pascal - Ответ 7495607

Существует еще несколько особенностей работы с модулями.

1. Описание в нескольких модулях одинакового идентификатора

2.

Циклическая зависимость модулей

Первая из них заключается в том, что при описании в нескольких модулях одинакового идентификатора (будь то имя типа, переменной, константы, или подпрограммы) и при подключении этих модулей друг к другу или к основной программе, при обращении к тем самым дублирующимся идентификаторам будет подразумеваться тот, который описан в

последнем

из подключенных модулей. Например, имеем:

Первый модуль:

PascalВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

unit u1;
 
interface
 
type 
  TestType = array[1 .. 10] of integer;
  
procedure TestProc;
 
implementation
 
procedure TestProc;
begin
  writeln('TestProc from u1');
end;
 
end.

Второй модуль:

PascalВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

unit u2;
 
interface
 
type
  TestType = integer;
  
procedure TestProc;
 
implementation
 
procedure TestProc;
begin
  writeln('TestProc from u2');
end;
 
end.

Основная программа:

PascalВыделить код

1
2
3
4
5

uses u1, u2;
 
begin
  TestProc; { Вывод: "TestProc from u2" }
end.

При сборке программы не будет никаких проблем, несмотря на одинаковые идентификаторы, а вот при запуске отработает та процедура, которая описана в модуле u2 (поскольку он подключен позже, чем u1). Если поменять порядок подключения модулей:

PascalВыделить код

1
2
3
4
5

uses u2, u1;
 
begin
  TestProc; { Вывод: "TestProc from u1" }
end.

, и пересобрать программу, то теперь отработает TestProc из модуля u1.

Для того, чтобы использовать идентификатор из модуля, подключенного НЕ последним, нужно указать, в каком именно модуле он описан:

PascalВыделить код

1
2
3
4
5

uses u2, u1;
 
begin
  u2.TestProc; { Теперь будет  вызвана процедура из u2, независимо от порядка подключения }
end.

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

PascalВыделить код

1
2
3
4
5
6
7
8

uses u1, u2;
 
var 
  i : integer;
  a : TestType;
begin
  for i := 1 to 10 do a[i] := i; { Ошибка, нелегальный квалификатор (Illegal Qualifier) }
end.

, хотя казалось бы, модуль подключен к программе, и все должно работать. Незнание этой особенности очень часто приводит к недоразумениям, особенно при использовании VCL/LCL или других библиотек совместно с модулем Windows. Например, как вот тут:

Создать объект Bitmap для загрузки изображения

Решение - точно такое же, как и в предыдущем случае. Либо поменять местами названия модулей в списке uses, либо использовать полное имя типа:

Еще одна особенность - подключение первого модуля ко второму, а второго - к первому.

Так называемая циклическая зависимость (circular unit reference). Вот пример (он, правда, синтетический, но основная идея понятна. При работе с теми же VCL/LCL тоже довольно часто встречается подобная ошибка, когда нужно связать две формы между собой) :

PascalВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

unit u1;
 
interface
uses u2;
 
procedure PrintFirst;
procedure PrintFirstSecond;
 
implementation
 
procedure PrintFirst;
begin
  writeln('Print first');
end;
procedure PrintFirstSecond;
begin
  writeln('Print first');
  PrintSecond;
end;
 
end.

PascalВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

unit u2;
 
interface
uses u1;
 
procedure PrintSecond;
procedure PrintSecondFirst;
 
implementation
 
procedure PrintSecond;
begin
  writeln('Print Second');
end;
procedure PrintSecondFirst;
begin
  writeln('Print Second');
  PrintFirst;
end;
 
end.

Основная программа

PascalВыделить код

1
2
3
4
5

uses u1, u2;
 
begin
  PrintFirstSecond;
end.

Этот проект компилироваться не будет, потому что присутствует циклическая зависимости: первый модуль ссылается на второй, а второй - на первый. Для того, чтобы решить проблему, достаточно перенести раздел Uses в любом из модулей (либо в обоих) из секции Interface в секцию Implementation:

PascalВыделить код

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

unit u1;
 
interface
 
procedure PrintFirst;
procedure PrintFirstSecond;
 
implementation
uses u2; { <--- Раньше было в Interface }
 
procedure PrintFirst;
begin
  writeln('Print first');
end;
procedure PrintFirstSecond;
begin
  writeln('Print first');
  PrintSecond;
end;
 
end.

, и проблема будет решена, программа компилируется и запускается. Когда хотя бы один из ссылающихся друг на друга модулей подключается в разделе Implementation, компилятор умеет разруливать такую зависимость.

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

Вернуться к обсуждению:

Подключаемые модули Pascal