Елементи програмування мовою PASCAL

 

        

·        Вступ

·        Основнi поняття

 

 

 

 

 


Идентификаторы и служебные слова

 

Идентификаторы служат в качестве имен программ, модулей, процедур, функций, типов, переменных и констант. Идентификатором считается любая последовательность латинских букв или цифр, начинающаяся с буквы. Буквой считается также символ подчеркивания "_".

Например, a1, _h, b123 - идентификаторы, а 1a, ф2 - нет.

 

Зарезервовані слова Borland Pascal   Таблиця 1.1

----------------------------------------------------------------

                              

and

exports

mod

shr

array

file

nil

string

asm

for

not

then

begin

function

object

to

case

goto

type

const

if

or

unit

consatructor

implementation

расked

until

destructor

in

procedure

uses

div

inherited

program

var

do

inline

record

while

downto

interface

repeat

with

else

label

set

xor

end

library

shl

                                                      

                                    

                                                               

                                                       

                                  

                                 

                                                 

                                  

                                                   

                        

 

 

 

 

 

 

 

 

 

Для Borland Pascal байдужий регістр клавіатури.

 

 

 

Структура програми

Програма на мові Pascal ABC має наступний вигляд:

program ім'я програми;
роздягнув підключення модуліврозділ описівbegin   операториend.

Перший рядок називається заголовком програми і не є обов'язковою.

Роздягнув підключення модулів починається із службового слова uses, за яким слідує список імен модулів, перераховуваних через кому.

Розділ описів може включати розділи опису змінних, констант, типів, процедур і функцій, які слідують один за одним в довільному порядку.

Роздягнув підключення модулів і розділ описів можуть бути відсутній.

Оператори відділяються один від одного символом "крапка з комою".

 

Выражения и операции

Любое выражение имеет определенный тип и после вычисления возвращает некоторое значение. Простейшими выражениями являются переменные и константы. Более сложные выражения строятся из более простых с использованием операций, скобок, вызовов функций, индексов и приведений типов. Данные, к которым применяются операции, называются операндами.

В Pascal ABC имеются следующие операции: @, not, ^, *, /, div, mod, and, shl, shr, +, -, or, xor, =, >, <, <>, <= и >=.

Арифметические операции

К арифметическим относятся бинарные операции + - * / для вещественных и целых чисел, бинарные операции div и mod для целых чисел и унарные операции + и - для вещественных и целых чисел. Выражение, имеющее числовой тип, называется арифметическим. Тип арифметического выражения определяется по следующему правилу: если все операнды целые и в выражении отсутствует операция деления /, то выражение имеет тип integer, в противном случае выражение имеет тип real. Например, если b имеет тип byte, c имеет тип 1..9, то b+c и -b имеют тип integer, а 2.0+b и 1/2 - тип real.

Логические операции

К логическим относятся бинарные операции and, or и xor, а также унарная операция not, имеющие операнды типа boolean и возвращающие значение типа boolean. Выражение, имеющее тип boolean, называется логическим.

Операции отношения

Операции отношения <, >, <=, >=, =, <> возвращают значение типа boolean и применяются к операндам целого, вещественного, символьного, логического и строкового типов, а также к типизированным указателям. Операции = и <> также применяются к операндам типа pointer, к операндам, являющихся объектами классов и к константе nil. При сравнении символа и строки символ преобразуется в строку длины 1.

Побитовые операции

Побитовые операции and, or, not, xor, shl, shr производят побитовые манипуляции с операндами целого типа. Для унарной операции not результат имеет тот же тип, что и операнд. Для остальных операций результат имеет тип integer. Например, если b имеет тип byte и b=1, то b shl 10=1024, а not b=254.

Строковые операции

Помимо операций отношения <, >, <=, >=, =, <>, к строковым и символьным операндам применима операция конкатенации (слияния) +. Ее результат имеет строковый тип. Например, 'a'+'b'='ab'. Поскольку строки могут содержать максимум 255 символов, то если сливаются строки суммарной длины больше 255, то программа завершается сообщением об ошибке

» Ошибка: произошло переполнение строки при выполнении операции "+".

Операция @

Операция @ применяется к переменной и возвращает ее адрес.

Операции с указателями

К типизированным указателям применима операция разыменования ^: если p является указателем на тип T, то p^ - элемент типа T, на который указывает p.

Помимо операций отношения <, >, <=, >=, =, <>, к типизированным указателям применимы арифметические операции + и -.

Операция + применяется к типизированному указателю и целому числу: если p имеет тип ^T и указывает на элемент a[k] одномерного массива элементов типа T, i - целое, то p+i возвращает адреc элемента a[k+i]. Операция - применяется к двум типизированным указателям одного типа: если p1 и p2 указывают на элементы одномерного массива элементов типа T, то p2-p1 возвращает количество элементов между этими указателями (взятое со знаком -, если адрес в p2 меньше адреса в p1). Например:

var
  a: array [1..10] of integer;
  p: ^integer;
begin
  p:=@a[1];
  for i:=1 to 10 do
  begin
    p^:=i;
    p:=p+1;
  end;
  ...

Операции с множествами

К множествам применимы операции + (объединение), - (разность) и * (пересечение):

var
  s1,s2,s: set of byte;
begin
  s1:=[1..4];
  s2:=[2..5];
  s:=s1+s2; // s=[1..5]
  s:=s1-s2; // s=[1]
  s:=s1*s2; // s=[2..4]

К множествам применимы также операции отношения = (равенство), <> (неравенство), <= (вложено) и >= (содержит):

var
  s1,s2,s: set of byte;
  b: boolean;
begin
  s1:=[1..4];
  s2:=[2..5];
  s:=[1,2]; 
  b:=s1=s2; // b=False
  b:=s2<>s; // b=True
  b:=s<=s2; // b=False
  b:=s1>=s; // b=True

Наконец, операция in определяет, принадлежит ли элемент множеству:

1 in [2..5] // False
3 in [2..5] // True

Приоритет операций

Приоритет определяет порядок выполнения операций в выражении. Первыми выполняются операции, имеющие высший приоритет. Операции, имеющие одинаковый приоритет, выполняются справа налево.

Таблица приоритетов операций

@, not, ^

1 (наивысший)

*, /, div, mod, and, shl, shr

2

+, -, or, xor

3

=, <>, <, >, <=, >=, in

4 (низший)

 

 

Описание переменных и констант

Раздел описания переменных начинается со служебного слова var, после которого следуют строки вида

список имен переменных: тип;

Имена в списке перечисляются через запятую. Например:

var
 
a,b,c: integer;
  d: real;
  e,f: integer;
  s,s1: string;
  ch: char;

Раздел описания именованных констант начинается со служебного слова const, после которого следуют строки вида

имя константы = значение;

или

имя константы : тип = значение;

Например:

const
  Pi = 3.14;
  Count = 10;
  Name = 'Mike';
  DigitsSet = ['0'..'9'];
  Arr: array [1..5] of integer = (1,3,5,7,9);
  Rec: record name: string; age: integer end = (name: 'Иванов'; age: 23);
  Arr2: array [1..2,1..2] of real = ((1,2),(3,4));

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

 

Описание типов

Раздел описания типов начинается со служебного слова type, после которого следуют строки вида

имя типа = тип;

Например,

type
 
myint = integer;
  arr10 = array [1..10] of integer;
  pinteger = ^integer;
  A = class
    i: integer;
    constructor Create(ii: integer);
    begin
      i:=ii;
    end;
  end;

При описании рекурсивных структур данных указатель на тип может фигурировать раньше описания самого типа в определении другого типа:

type
  PNode = ^TNode;
  TNode = record
    data: integer;
    next: PNode;
  end;

При этом важно, чтобы определения обоих типов находились в одном разделе type.

Следующее описание, однако, ошибочно:

type
  TNode = record
    data: integer;
    next: ^TNode;
  end;

(сообщение об ошибке: "Тип TNode полностью не определен").

 

Область действия идентификатора

Основная программа, процедура и функция состоят из заголовка и блока. Блоком называется раздел описаний, после которого следуют операторы, заключенные в операторные скобки begin/end.

Любой используемый в блоке идентификатор должен быть предварительно описан. В одном блоке не может быть описано двух переменных, констант или типов с одним именем.

В блоке, однако, может быть описано несколько процедур или функций с одним именем, но с разным набором параметров (это называется перегрузкой имен).

Область действия идентификатора (т.е. место, где он может быть использован) простирается от момента описания до конца блока, в котором он описан. Область действия глобальноого идентификатора, описанного в модуле, простирается на весь модуль, а также на основную программу, к которой данный модуль подключен в разделе uses.

Идентификатор с тем же именем, определенный во вложенном блоке, скрывает идентификатор, определенный во внешнем блоке. Например, в коде

var i: integer;

procedure p;
var i: integer;
begin
  i:=5;
end;

значение 5 будет присвоено переменной i, описанной в процедуре p; внутри же процедуры p сослаться на глобальную переменную i невозможно.

Аналогичная ситуация имеет место и в производных классах: в них можно определять поля с теми же именами, что и в базовых классах, но тогда ссылаться на поле базового класса из метода производного класса нельзя. Однако, можно ссылаться на метод базового класса из метода производного класса, используя служебное слово inherited:

type
  A=class
    i: integer;
    procedure p;
    begin
      i:=5;
    end;
  end;
  B=class(A)
    i: integer;
    procedure p;
    begin
      i:=5;
      inherited p;
    end;
  end;

 

Ввод и вывод. Форматы вывода

Для вывода в окно вывода используются стандартные процедуры write и writeln. Они могут вызываться как без параметров, так и со списком параметров. Параметры в списке перечисляются через запятую и должны иметь простой тип (кроме перечислимого типа и интервального типа, построенного на базе перечислимого), либо тип string, либо тип указателя. Процедура writeln после вывода своих параметров осуществляет переход на следующую строку.

В процедурах вывода write и writeln после каждого выводимого значения типа может указываться формат вывода, представляющий собой двоеточие, после которого следует целое число или выражение. Это число или выражение задает ширину поля вывода, то есть количество позиций, отводимых под выводимое значение. Если длина выводимого значения меньше ширины поля вывода, то выводимый текст дополняется слева пробелами до нужной ширины; в результате выводимое значение выравнивается по правому краю. Если длина выводимого значения больше ширины поля вывода, то формат вывода игнорируется. Вещественные и комплексные значения с форматом вывода вида :m всегда выводятся в экспоненциальной форме.

Например, если a, b - целые переменные, то при выполнении операторов

a:=-2437; b:=13555;
writeln(a:6,'Привет!':9);
writeln(b:1);

в окно вывода будет выведен следующий текст:

 -2437  Привет!
13555

Для вещественных и комплексных значений можно также использовать формат :m:n, где m и n - целые значения. Значение m задает ширину поля вывода, а значение n  количество знаков после десятичной точки. Например:

writeln(-14.859:10:3); // ___-14.859
writeln(-14.859:10:5); // _-14.85900
writeln(-14.859:10:2); // ____-14.86
writeln(-14.859:10:0); // _______-15
writeln(-14.859:10:7); // -14.8590000
writeln((0,1):10:1);   // _(0.0,1.0)

(здесь символом _ изображены пробелы).

Для ввода с клавиатуры используются стандартные процедуры read и readln. Они могут вызываться как без параметров, так и со списком параметров. Параметры в списке перечисляются через запятую и должны быть переменными простого типа (кроме перечислимого типа и интервального типа, построенного на базе перечислимого), либо типа string. Процедура readln после ввода пропускает данные до конца текущей строки ввода.

Процедуры write, writeln, read, readln - единственные, для которых можно указывать список параметров произвольной длины.

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

writeln(f,'abc',1,True,2.4);

 

 

Оператори

Оператор присваивания

Оператор присваивания имеет вид:

переменная:= выражение

В качестве переменной может быть простая переменная, разыменованный указатель, переменная с индексами или компонент переменной типа запись. Выражение должно иметь тип, либо совпадающий с типом переменной, либо неявно к нему приводящийся (см. Неявное приведение типов).

Условный оператор

Условный оператор имеет полную и краткую формы.

Полная форма условного оператора выглядит следующим образом:

if условие then оператор1
else оператор2

В качестве условия указывается некоторое логическое выражение. Если условие оказывается истинным, то выполняется оператор1, в противном случае выполняется оператор2.

Краткая форма условного оператора имеет вид:

if условие then оператор

Если условие оказывается истинным, то выполняется оператор, в противном случае происходит переход к следующему оператору программы.

В случае конструкции вида

if условие1 then
  if
условие2 then оператор1
  else оператор2

else всегда относится к ближайшему предыдущему оператору if, для которого ветка else еще не указана. Если в предыдущем примере требуется, чтобы else относилась к первому оператору if, то необходимо использовать составной оператор:

if условие1 then
begin

  if
условие2 then оператор1
end

else оператор2

Составной и пустой операторы

Составной оператор предназначен для объединения нескольких операторов в один. Он имеет вид:

begin
 
операторы
end

Операторы отделяются один от другого символом ";". Служебные слова begin и end, окаймляющие операторы, называются операторными скобками.

Например:

s:=0; p:=1;
for i:=1 to 10 do
begin
 
p:=p*i;
  s:=s+p
end

Перед end также может ставиться ";". В этом случае считается, что последним оператором перед end

Оператор выбора

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

case переключатель of
  список выбора 1: оператор1;
  ...
  список выбора N: операторN;
  else
оператор0
end;

Переключатель представляет собой выражение порядкового типа (целого, символьного, перечислимого или интервального), а списки выбора содержат константы совместимого типа. Как и в операторе if, ветка else может отсутствовать.

Оператор case работает следующим образом. Если в одном из списков выбора найдено текущее значение переключателя, то выполняется оператор, соответствующий данному списку. Если же значение переключателя не найдено ни в одном списке, то выполняется оператор по ветке else или, если ветка else отсутствует, оператор case не выполняет никаких действий.

Список выбора состоит либо из одной константы, либо из диапазона значений вида a..b (константа a должна быть меньше константы b); можно также перечислить несколько констант или диапазонов через запятую:

case DayOfWeek of
  1..5: writeln('Будний день');
  6,7: writeln('Выходной день');
end;

Списки выбора не должны пересекаться. Например, следующий фрагмент

case i of
  2,5: write(1);
  4..6: write(2);
end;

приведет к ошибке компиляции "Пересечение диапазонов меток в операторе case".

 

Оператор цикла for

Оператор цикла for имеет одну из двух форм:

for переменная:=начальное значение to конечное значение do
  оператор

или

for переменная:=начальное значение downto конечное значение do
  оператор

Текст от слова for до слова do включительно называется заголовком цикла, а оператор после do - телом цикла. Переменная после слова for называется параметром цикла. Для первой формы цикла с ключевым словом to параметр цикла меняется от начального значения до конечного значения, увеличиваясь всякий раз на единицу, а для второй формы ключевым словом downto - уменьшаясь на единицу. Для каждого значения переменной-параметра выполняется тело цикла. Однократное повторение тела цикла называется итерацией цикла.

Значение параметра цикла после завершения цикла считается неопределенным.

Если для цикла for ... to начальное значение переменной цикла больше конечного значения или для цикла  for ... downto начальное значение переменной цикла меньше конечного значения, то тело цикла не выполнится ни разу.

Если цикл используется в процедуре или функции, то переменная-параметр цикла должна быть описана как локальная.

Точка с запятой, следующая сразу после do, в Pascal ABC считается синтаксической ошибкой.

Изменение переменной-параметра цикла внутри цикла является логической ошибкой. Например, следующий фрагмент со вложенным оператором for является ошибочным:

for i:=1 to 10 do
  for i:=1 to 5 do
    write(i);

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

var
  en: (red,green,blue,white);
  c: char;
...
for
en:=red to blue do
  write(Ord(en):2);
for c:='a' to 'z' do
  write(c);
 

 Оператор цикла while

Оператор цикла while имеет следующую форму:

while условие do
  оператор

Условие представляет собой выражение логического типа, а оператор после do называется телом цикла. Перед каждой итерацией цикла условие вычисляется, и если оно истинно, то выполняется тело цикла, в противном случае происходит выход из цикла.

Если условие всегда оказывается истинным, то может произойти зацикливание:

while 2>1 do
  write(1);

Чтобы прервать зациклившуюся программу, следует использовать комбинацию клавиш Ctrl-F2 или кнопку .

Точка с запятой сразу после do в Pascal ABC считается синтаксической ошибкой.

Оператор цикла repeat

Оператор цикла repeat имеет следующую форму:

repeat
  операторы
until
условие

В отличие от цикла while, условие вычисляется после очередной итерации цикла, и если оно истинно, то происходит выход из цикла. Таким образом, операторы, образующие тело цикла оператора repeat, выполняются по крайней мере один раз.

Если условие всегда оказывается ложным, то может произойти зацикливание:

repeat
  write(1);
until 2=1;

Чтобы прервать зациклившуюся программу, следует использовать комбинацию клавиш Ctrl-F2 или кнопку .

 Оператор вызова процедуры

Оператор вызова процедуры имеет вид:

имя процедуры

или

имя процедуры(список фактических параметров)

Количество фактических параметров должно совпадать с количеством формальных, а типы фактических параметров должны соответствовать типам соответствующих формальных.

Операторы break, continue и exit

Операторы break и continue используются только внутри циклов.

Оператор break предназначен для досрочного завершения цикла. При его выполнении происходит немедленный выход из текущего цикла и переход к выполнению оператора, следующего за циклом. Оператор continue завершает текущую итерацию цикла, осуществляя переход к концу тела цикла. Например:

flag:=False;
for i:=1 to 10 do
begin
  read(x);
  if x<0 then continue; // пропуск текущей итерации цикла
  if x=5 then
  begin
    flag:=True;
    break; // выход из цикла
  end
end;

Использование операторов break и continue вне тела цикла ошибочно.

Оператор exit предназначен для досрочного завершения процедуры или функции. Например

function Analyze(x: integer): boolean;
begin
  if x<0 then
  begin
    Result:=False;
    exit
  end;
  ...
end;

Вызов exit в разделе операторов основной программы приводит к ее немедленному завершению.

Следует отметить, что в Pascal ABC, в отличие от Borland Pascal и Borland Delphi, break, continue и exit являются не операторами, а процедурами и входят в число служебных слов языка.

Оператор with

Оператор with позволяет сократить обращение к полям записи, а также к полям, методам и свойствам объекта. Он имеет вид:

with имя записи или объекта do оператор

или

with список имен do оператор

Всюду внутри оператора можно опускать имя записи при обращении к полю указанной записи или имя объекта при обращении к полю, методу или свойству указанного объекта. Например, пусть описана переменная

var
  DateOfBirthday=record
    Day: Integer;
    Month: Integer;
    Year: Integer;
  end;

Тогда присваивание значений ее полям без использования оператора with имеет вид:

DateOfBirthday.Day:=23;
DateOfBirthday.Month:=2;
DateOfBirthday.Year:=1965;

Использование оператора with позволяет сократить предыдущую запись:

with DateOfBirthday do
begin
  Day:=23;
  Month:=2;
  Year:=1965; 
end;

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

var
  x,y,z: integer;
  a: record
    x,y: integer;
  end;
  b: record
    x: integer;
  end;

то фрагмент программы

with a,b do
begin
  x:=1;
  y:=2;
  z:=3; 
end;

эквивалентен фрагменту

with a do
 with b do
 begin
   x:=1;
   y:=2;
   z:=3; 
 end;

а также фрагменту

b.x:=1;
a.y:=2;
z:=3;

 

 

 

Типи даних

 

Обзор типов

В Pascal ABC имеются следующие типы:

integer (целый)
byte (байтовый)
char (символьный)
перечислимый
диапазонный
boolean (логический)
real (вещественный)
complex (комплексный)
string (строковый)
тип "массив"
тип "запись"
тип "указатель"
процедурный
файловый
классовый

Типы integer, byte, char, перечислимый и диапазонный называются порядковыми. Только значения этих типов могут быть индексами массивов и фигурировать в качестве выражения-переключателя в операторе case. Переменная-параметр цикла for также должна иметь перечислимый тип.

Ко всем значениям порядкового типа применимы следующие функции:

Pred(x) возвращает значение, предшествующее x (к наименьшему значению не применяется);
Succ(x) возвращает значение, следующее за x (к наибольшему значению не применяется);
Ord(x) возвращает порядковое целое значение, соответствующее x. Для целых x возвращает само значение x, для символов char возвращает их код, а для элементов перечислимого типа - их номер (нумерация начинается с нуля).

Все порядковые типы, а также типы boolean, real и complex называются простыми типами.


Порядковые типы

Тип integer (целый). Значения этого типа занимают 4 байта и находятся в диапазоне от -2147483648 до 2147483647. Константа MaxInt хранит значение 2147483647.

Тип byte (беззнаковый целый). Значения этого типа занимают 1 байт и находятся в диапазоне от 0 до 255.

Тип char (символьный). Значения этого типа занимают 1 байт и представляют собой символы в кодировке Windows. Стандартная функция Chr(x) возвращает символ с кодом x. Константы этого типа могут быть записаны в виде #x, где x - целое число от 0 до 255.

Перечислимый тип определяется упорядоченным набором идентификаторов:

type
 
Season = (Winter,Spring,Summer,Autumn);
  DayOfWeek = (Mon,Tue,Wed,Thi,Thr,Sat,Sun);

Значения перечислимого типа занимают 4 байта.

Интервальный тип представляет собой подмножество значений целого, символьного или перечислимого типа и описывается в виде a..b, где a - нижняя, b - верхняя граница интервального типа:

var
 
a: 0..10;
  c: 'a'..'z';
  d: Mon..Thr; 

Тип, на основе которого строится интервальный тип, называется базовым

Типы boolean, real и complex

Тип boolean (логический). Переменные и константы логического типа занимают 1 байт и принимают одно из двух значений, задаваемых предопределенными константами True (истина) и False (ложь).

Тип real (вещественный). Значения вещественного типа занимают 8 байт, содержат 15-16 значащих цифр и по модулю не могут превосходить величины 1.7∙10308. Самое маленькое положительное число вещественного типа равно 5.0∙10-324. Константы типа real можно записывать как в форме с плавающей точкой, так и в экспоненциальной форме: 1.7, 0.013, 2.5e3 (2500), 1.4e-1 (0.14).

Тип complex (комплексный). Значения комплексного типа занимают 16 байт. Константы этого типа записываются в виде (x,y), где x и y - выражения вещественного типа, представляющие собой вещественную и мнимую части комплексного числа. Если переменная c имеет тип complex, то обратиться к ее вещественной и мнимой частям можно как к полям записи: c.re и c.im. Например:

const ci=(0,1);
var c: complex;
...
c.re:=2*c.im;
c:=ci*c+(c.im,c.re);

К переменным типа complex применимы стандартные числовые функции: abs, sin, cos, exp, ln, sqrt, а также функция conj(c), возвращающая комплексно сопряженное к c, и функция carg(c), возвращающая главное значение аргумента комплексного числа c. При вычислении многозначных функций ln, sqrt, carg возвращается главное значение и предполагается, что разрез сделан по отрицательной вещественной оси, причем, верхний берег принадлежит разрезу. Так, carg(c) возвращает значение в диапазоне (-Pi,Pi].

 Строковый тип

Строки в Pascal ABC имеют тип string и состоят из не более чем 255 символов. При описании

var s: string;

под переменную s отводится 256 байт, при этом в нулевом байте хранится длина строки.

Для экономии памяти предусмотрено описание вида

var s1: string[40];

В этом случае под строку отводится 41 байт (нулевой байт - под длину строки). В случае присваивания переменной s1 строки из более чем 40 символов лишние символы отсекаются, и длина строки s1 полагается равной 40.

При выполнении операции конкатенации вида s1+s1 результат считается строкой типа string, т.е.занимает 256 байт. Однако, если при конкатенации результат будет занимать более 255 символов, то программа завершится с ошибкой.

К символам в строке можно обращаться, используя индекс: s[i] обозначает i-тый символ в строке. Обращение к нулевому символу s[0] считается ошибочным. Чтобы изменить длину строки, следует воспользоваться процедурой SetLength. Если индекс i выходит за пределы памяти, отводимой под строку, то выдается сообщение об ошибке. Однако, если индекс i выходит лишь за пределы длины строки, то сообщение об ошибке не выдается.

 Массивы

Массив представляет собой набор элементов одного типа, каждый из которых имеет свой номер, называемый индексом (индексов может быть несколько, тогда массив называется многомерным). Тип массива конструируется следующим образом:

array [тип индекса1, ..., тип индексаN] of базовый тип

Тип индекса обязательно представляет собой интервальный тип и обязательно должен задаваться в виде a..b, где a и b - константные выражения целого, символьного или перечислимого типа. Например:

type enum=(w1,w2,w3,w4,w5);
var
  a1,a2: array [1..10] of integer;
  b: array ['a'..'z',w2..w4] of string;
  c: array [1..10] of array [1..5] of real;

Базовый тип может быть любым (в частности, он может быть типом массива, как для переменной c).

Переменные-массивы одного типа можно присваивать друг другу, при этом будет производиться копирование содержимого одного массива в другой:

a1:=a2;

При передаче по значению параметра типа массив в подпрограмму также производится копирование содержимого массива-фактического параметра в массив-формальный параметр:

procedure p(arr: array [1..10] of integer);
...
p(a1);

Как правило, в этой ситуации копирование содержимого массива не требуется, поэтому массив рекомендуется передавать по ссылке:

procedure r(var arr: array [1..10] of integer);
...
r(a1);

К элементам массива обращаются при помощи переменных с индексами:

a1[3]:=a2[5];
b['f',w3]:='Hello';
c[3][4]:=3.14;

Наиболее часто встречающаяся ошибка при работе с массивами - выход за границы изменения индекса. В Pascal ABC возникновение этой ошибки приводит к завершению работы программы.

Записи

Запись представляет собой набор элементов разных типов, каждый из которых имеет свое имя и называется полем записи. Тип записи конструируется следующим образом:

record список полей1: тип1; ... список полейN: типN; end

Последний символ ";" не является обязательным. Например:

type
  Date=record
    Day: 1..31;
    Month: (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);
    Year: Integer;
  end;

Теперь можно объявить переменные типа Date:

var d1,d2: Date;

Чтобы получить доступ к полям записи, следует воспользоваться точечной нотацией, указав имя переменной-записи и поле, разделенные точкой:

d1.Day:=9;
d1.Month:=Sep;
d1.Year:=2004;

Как и для массивов, можно скопировать содержимое полей одной переменной-записи в другую:

d2:=d1;
 

Множества

Множество представляет собой набор элементов одного порядкового типа. Элементы множества считаются неупорядоченными; каждый элемент может входить во множество не более одного раза. Тип множества описывается следующим образом:

set of базовый тип

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

type
  ByteSet = set of byte;
  CharSet = set of char;
  Digits = set of '0'..'9';
  SeasonSet = set of (Winter,Spring,Summer,Autumn);

Переменная типа множество занимает 16 байт и может содержать несколько значений базового типа. Чтобы сконструировать значение типа множество, используется конструкция вида

[список значений]

где в списке могут перечисляться через запятую либо выражения базового типа, либо их диапазоны в виде a..b, где a и b - выражения базового типа. Например:

var
  bs: ByteSet;
  bestdays: (Mon,Tue,Wed,Thi,Thr,Sat,Sun);
...
bs:=[1,3,5,20..25];
bestdays:=[Wed,Thr..Sun];

Значения в списке могут отсутствовать, тогда множество является пустым:

bs:=[];

Операция in проверяет принадлежность элемента множеству:

if Wed in bestdays then ...

Для множеств определены операции + (объединение), - (разность), * (пересечение), = (равенство), <> (неравенство), <= (вложено) и  и >= (содержит). Примеры этих операций приведены в пункте Выражения и операции.

Множества с совместимыми базовыми типами неявно преобразуются одно к другому (см. пункт Неявное приведение типов). При присваивании множеству при необходимости происходит усечение его элементов до значений базового типа. Например:

var st: set of 3..9;
...
st:=[1..5,8,10,12]; // в st попадут значения [3..5,8]

Чтобы вывести значения элементов множества, необходимо перебрать всевозможные значения в цикле и проверить их на принадлежность множеству с помощью операции in:

for i:=0 to 255 do
  if i in st then write(i,' ');

Указатели

Указатель хранит адрес памяти. В Pascal ABC имеются типизированные указатели (содержат адрес ячейки памяти данного типа) и бестиповые указатели (содержат адрес оперативной памяти, не связанный с данными какого-либо определенного типа).

Тип указателя на тип T имеет форму ^T, например:

type pinteger=^integer;
var p: ^record r,i: real end;

Бестиповой указатель описывается с помощью слова pointer.

Для доступа к ячейке памяти, адрес которой хранит типизированный указатель, используется операция разыменования ^:

var
  i: integer;
  pi: ^integer;
...
pi:=@i; // указателю присвоили адрес переменной i
pi^:=5; // переменной i присвоили 5

Операция разыменования не может быть применена к бестиповому указателю.

Типизированный указатель может быть неявно преобразован к бестиповому:

type preal=^real;
var

  p: pointer;
  pr: ^real;
...
p:=pr;

Обратное преобразование может быть выполнено только явно:

pr:=preal(p);
pr^:=3.14;

Указатели можно сравнивать на равенство (=) и неравенство (<>). В дополнение к этому типизированные указатели можно сравнивать, используя операции <, >, <=, >=.

Для типизированных указателей доступны также операции p+i, p-i и p2-p1, где i - целое (см.п.Выражения и операции).

Для того чтобы отметить тот факт, что указатель никуда не указывает

Файлы

Файл в Pascal ABC представляет собой последовательность элементов одного типа, хранящихся на диске. В Pascal ABC имеется два типа файлов - типизированные и текстовые. Текстовые файлы хранят символы, разделенные на строки символами #13#10.

Для описания текстового файла используется стандартное имя типа text, а для описания типизированного файла - конструкция file of тип элементов:

var
  f1: file of real;
  f2: text;

Файловые процедуры и функции описываются в пункте Процедуры и функции для работы

Процедурный тип

Переменные, предназначенные для хранения процедур и функций, называются процедурными. Тип процедурной переменной имеет вид:

procedure (список параметров)

или

function (список параметров): тип возвращаемого значения

При этом список параметров вместе с круглыми скобками могут отсутствовать. Например:

type proc=procedure(i: integer);
var
  p1: proc;
  p2: procedure;
  f1: function: integer;

Процедурной переменной можно присвоить процедуру или функцию с совместимым типом:

procedure my(i: integer);
begin
  ...
end;
function f: integer;
begin

end;
...
p1:=my;
f1:=f;

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

p1(5);
write(f1);

Обычно процедурные переменные передаются как параметры для реализации обратного вызова:

procedure foreach(var a: array [1..100] of real; n: integer; p: procedure(var r: real));
var i: integer;
begin
  for i:=1 to n do
    p(a[i]);
end;

procedure mult2(var r: real);
begin
  r:=2*r
end;

procedure print(var r: real);
begin
  write(r,' ');
end;
...
foreach(a,10,mult2); // умножение элементов массива на 2
foreach(a,10,print); // вывод элементов массива

 

Эквивалентность типов

В Pascal ABC два типа считаются эквивалентными (равными), если совпадают их описания. Например, после описаний

type Arr=array [1..100] of integer;
var

  a: Arr;
  b: array [1..100] of integer;

можно присваивать массивы друг другу:

a:=b;

В типах записей, помимо типов полей, должны совпадать имена:

type
  rec1 = record i,j: integer end;
  rec2 = record x,y: integer end;
...
var r1: rec1; r2: rec2;
...
r1:=r2; // ошибка!

В типах формальных параметров процедур и функций имена могут не совпадать:

type
  proc1 = procedure (i: integer); 
  proc2 = procedure (x: integer);
...
var p1: proc1; p2: proc2;
...
p1:=p2; // все в порядке

Подобная расширенная трактовка эквивалентности (по сравнению с Borland Pascal и Borland Delphi) позволяет передавать параметры составного типа (массивы, записи, процедурный и указательный) в процедуры и функции, не определяя для этих параметров новый тип:

procedure foreach(var a: array [1..100] of real; n: integer; f: function(r: real): real);
var i: integer;
begin
  for i:=1 to n do
    a[i]:=f(a[i]);
end;

 

Неявное приведение типов

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

Типы byte и integer неявно преобразуются друг к другу, а также к типам real и complex.

Тип real неявно преобразуется к типу complex.

Интервальный тип и его базовый тип неявно преобразуется друг к другу, неявно преобразуются между собой также интервальные типы, построенные на основе одного базового типа.

Символ неявно преобразуется в строку. Строки разной длины неявно преобразуются друг к другу.

Указатель на любой тип неявно преобразуется к указателю pointer.

Производный класс неявно преобразуется к базовому.

Множество неявно преобразуется к другому множеству, если его базовый тип неявно преобразуется к базовому типу этого другого множества.

Имя процедуры или функции неявно преобразуется к указателю на процедуру или функцию с параметрами тех же типов (у функции должен также совпадать тип возвращаемого значения).

Все возможные неявные преобразования типов проиллюстрированы в программе ImplicitCast.pas.

В процессе выполнения программы после неявного преобразования числовых типов может произойти переполнение. Например, это возможно при преобразовании типа integer в тип byte. Однако, если s1 и s2 - строки и при присваивании s1:=s2 данные строки s2 не умещаются в строку s1, то остаток строки усекается без возникновения ошибки.

Операнды в выражениях также могут иметь разные типы. В этом случае обычно меньший тип неявно приводится к большему или оба типа приводятся к большему. Например, если r имеет тип real, а i - тип integer, то в выражении r+i значение i вначале приводится к типу real, после чего выполняется операция сложения над двумя вещественными значениями. Если строка s1 имеет тип string[30], а s2 - тип string[40], то в выражении s1+s2 обе строки приводятся к типу string и только потом выполняется операция сложения.

Выражения "меньший тип" и "больший тип" в предыдущем абзаце используются в смысле диапазона значений. Например, тип set of 1..9 меньше типа set of byte, но ни один из типов set of 1..9 и set of 5..15 не меньше другого.

Явное приведение типов

Некоторые типы не приводятся к другим неявно, однако такое преобразование имеет смысл. Например, переменную типа char можно преобразовать к значению типа byte, являющемуся кодом символа. Переменную типа integer, содержащую неотрицательное значение, можно преобразовать к интервальному типу, причем, это значение будет выступать номером элемента в порядковом типе.

Для преобразования выражения к новому типу используется конструкция

имя нового типа(выражение)

Например:

type
  pinteger=^integer;
  days=(mon,tue,wed,thi,fri,sat,sun);
var
  p: pointer;
  i: integer;
  en: days;
...
p:=pinteger(@i);
i:=integer(en):
en:=days(3);

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

p:=^integer(@i);

Явные преобразования типов содержат все неявные. Кроме того, все порядковые типы можно явно преобразовывать друг к другу, все порядковые типы можно явно преобразовывать к типам real и complex, базовый класс можно явно преобразовать к производному и указатель pointer можно явно преобразовать к указателю на любой тип.

Все возможные явные преобразования типов проиллюстрированы в программе ExplicitCast.pas.

 

 

 

 

Процедури і функції

 

Описание процедур и функций

Процедура или функция представляет собой последовательность операторов, которая имеет имя, список параметров и может быть вызвана из различных частей программы. Функции, в отличие от процедур, в результате своего выполнения возвращают значение. Для единообразия функции и процедуры называются подпрограммами.

Любая используемая в программе процедура или функция должна быть предварительно описана в разделе описаний.

Описание процедуры имеет вид:

procedure имя(список формальных параметров);
раздел описаний
begin
  операторы
end;

Описание функции имеет вид:

function имя(список формальных параметров): тип возвращаемого значения;
раздел описаний
begin
  операторы
end;

Операторы подпрограммы, окаймленные операторными скобками begin/end, называются телом этой подпрограммы.

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

Раздел описаний процедуры или функции устроен так же, как и раздел описаний основной программы. Здесь описываются так называемые локальные переменные и константы, типы а также вложенные процедуры и функции. Все такие локальные объекты доступны лишь внутри данной подпрограммы и не видны извне.

Пример описания процедуры приводится ниже:

procedure Reverse(var a: array [1..100] of integer; n: integer);
var i,v: integer;
begin
  for i:=1 to n div 2 do
  begin
    v:=a[i];
    a[i]:=a[n-i+1];
    a[n-i+1]:=v;   
  end;
end;

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

function Add(a,b: real): real;
begin
  Add:=a+b;
end;

Имя функции может быть использовано с целью возврата значения только в левой части оператора присваивания. Если имя функции встречается в выражении, то это трактуется как рекурсивный вызов этой функции.

Вместо имени функции, внутри тела функции можно использовать другую специальную переменную с именем Result. В отличие от имени функции, переменную Result можно использовать и в выражениях:

function MinElement(var a: array [1..100] of real; n: integer): real;
var i: integer;
begin
  Result:=a[1];
  for i:=1 to n do
    if a[i]<Result then Result:=a[i];
end;

Если внутри функции не присвоить имени функции или переменной Result некоторое значение, то функция вернет в результате своего вызова непредсказуемое значение.

Параметры процедур и функций

Параметры, указываемые при описании подпрограммы, называются формальными. Параметры, указываемые при вызове подпрограммы, называются фактическими.

Если формальный параметр описан со служебным словом var, то его называют параметром-переменной и говорят, что он передается по ссылке. Если же параметр описан без слова var, то его называют параметром-значением и говорят, что он передается по значению.

Если параметр передается по значению, то при вызове подпрограммы значения фактических параметров присваиваются соответствующим формальным параметрам. Например, пусть имеется следующее описание процедуры:

procedure PrintSquare(i: integer);
begin
  writeln(i*i);
end;

Тогда при вызове PrintSquare(5*a-b) значение 5*a-b будет вычислено и присвоено переменной i, после чего выполнится тело процедуры.

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

procedure Mult2(var a: integer);
begin
  a:=a*2;
end;

то после вызова Mult(d) значение d увеличится в 2 раза.

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

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

procedure Print(var a: array [1..10] of integer; n: integer);
var i: integer;
begin
  for i:=1 to n do
    write(a[i],' ');
  writeln;
end;

Параметр может быть также описан со служебным словом const. Это означает, что он передается по значению, но менять его в подпрограмме запрещено. Например:

procedure PrintSquare1(i: integer);
begin
  i:=i*i; // верно
  writeln(i);
end;
procedure PrintSquare
2(const i: integer);
begin
  i:=i*i; // ошибка!!!
  writeln(i);
end;

 

Локальные и глобальные переменные

Переменные, описанные в разделе описаний подпрограммы, называются ее локальными переменными. Переменные же, описанные вне подпрограммы, называются глобальными по отношению к ней.

Параметры подпрограммы считаются ее локальными переменными.

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

var i: real;
procedure p;
var i: integer;
begin
  // к глобальной переменной i внутри процедуры нельзя обратиться
  i:=5; // присваивание локальной переменной i; ...
end;

 

Опережающее объявление

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

procedure B(i: integer); forward;
procedure A(i: integer);
begin
  ...
  B(i-1);
end;
procedure B(i: integer);
begin
  ...
  A(i div 2);
end;

Forward-объявление возможно только для внешних подпрограмм. Запрещено также делать forward-объявление для уже описанной подпрограммы.

 

Перегрузка имен

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

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

procedure p(b: byte);
begin

end;
procedure
p(r: real);
begin

end;

то при вызове p(1.0) будет выбрана перегруженная версия с параметром типа real (точное соответствие), а при вызове p(1) будет выбрана перегруженная версия с параметром типа byte (при этом произойдет преобразование фактического параметра типа integer к типу byte).

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

procedure p(i: integer; r: real);
begin

end;
procedure
p(r: real; i: integer);
begin

end;

то при вызове p(1,2) оба они одинаково подходят, что приводит к неоднозначности.

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

Перегрузка в Pascal ABC не действует, если подпрограммы с одинаковыми именами находятся в разных пространствах имен. Например, если в базовом классе имеются методы с теми же именами, что и в производном, то алгоритм поиска перегруженной версии для производного класса таков: вначале ищется перегруженная версия в производном классе, и если она не найдена, то поиск производится в его базовом классе и т.д. Например, рассмотрим следующий код:

type
 
A=class
    procedure
p(i: integer);
    procedure
q(r: real);
  end;
 
B=class(A)
    procedure
p(r: real);
    procedure
q(i: integer);
  end;
...
var
m: B;
...
m.p(1);  
m.q(2.1);

В первом случае вызовется метод p класса B, т.к. соответствие типа параметра будет найдено уже в классе B (хотя оно и не точное - требуется преобразование от типа integer к типу real). Во втором же случае метод q, который может быть вызван с вещественным параметром, в классе B не будет найден, и поиск продолжится в классе A; таким образом, вызовется метод q класса A.
Такое правило разрешения перегрузки для подклассов отличается от принятого в Delphi и C++. В C++, например, метод в производном классе полностью скрывает одноименные методы базового класса. В Delphi в перегрузке участвуют все методы базовых классов за исключением методов с идентичными списками параметров. Для не методов поиск перегруженной подпрограммы в Pascal ABC ограничивается разделом описаний, где впервые встречено такое имя.

 

 

Стандартні процедури і функції

 

Общие стандартные процедуры и функции

Имя и параметры

Процедура или функция

Типы параметров

Тип возвращаемого значения

Действие

Read(a,b,...)

процедура

a,b,c - переменные простого типа или типа string

 

вводит значения с клавиатуры в переменные a, b ...

Write(a,b,...)

процедура

a,b,c - выражения простого типа, типа string или указатели

 

выводит значения a, b ... в окно вывода

Readln(a,b,...)

процедура

a,b,c - переменные простого типа или типа string

 

вводит значения с клавиатуры в переменные a, b ..., после чего пропускает все оставшиеся символы в текущей строке ввода. Если параметры процедуры не указаны, то осуществляет паузу в выполнении программы до нажатия клавиши Enter

Writeln(a,b,...)

процедура

a,b,c - выражения простого типа, типа string или указатели

 

выводит значения a, b ... в окно вывода и осуществляет переход на следующую строку. Если параметры процедуры не указаны, то выполняет только переход на следующую строку.

Abs(x)

функция

x - integer, real, complex

совпадает с типом параметра

возвращает абсолютное значение (модуль) x

Sqr(x)

функция

x - integer, real, complex

совпадает с типом параметра

возвращает квадрат x

Sqrt(x)

функция

x - real, complex

совпадает с типом параметра

возвращает квадратный корень из x

Sin(x)

функция

x - real, complex

совпадает с типом параметра

возвращает синус x

Cos(x)

функция

x - real, complex

совпадает с типом параметра

возвращает косинус x

Ln(x)

функция

x - real, complex

совпадает с типом параметра

возвращает натуральный логарифм x

Exp(x)

функция

x - real, complex

совпадает с типом параметра

возвращает e в степени x (e=2.718281...)

Arctan(x)

функция

x - real, complex

совпадает с типом параметра

возвращает арктангенс x

Power(x,y)

функция

x, y - real

real

возвращает x в степени y

Conj(z)

функция

z - complex

complex

возвращает число, комплексно сопряженное к z

Carg(z)

функция

z - complex

real

возвращает аргумент комплексного числа z (в диапазоне (-Pi, Pi])

Round(x)

функция

x - real

integer

возвращает результат округления x до ближайшего целого

Trunc(x)

функция

x - real

integer

возвращает целую часть х

Int(x)

функция

x - real

real

возвращает целую часть х

Frac(x)

функция

x - real

real

возвращает дробную часть х

Ord(x)

функция

x - порядкового типа

integer

возвращает номер значения порядкового типа

Chr(x)

функция

x - integer

char

возвращает символ с кодом х

Odd(x)

функция

x - integer

boolean

возвращает True, если х - нечетное, и False в противном случае

Inc(x)

процедура

x - порядкового типа

 

Увеличивает x на 1

Dec(x)

процедура

x - порядкового типа

 

Уменьшает x на 1

Inc(x,n)

процедура

x - порядкового типа,
n
- целого типа

 

Увеличивает x на n

Dec(x,n)

процедура

x - порядкового типа,
n
- целого типа

 

Уменьшает x на n

Pred(x)

функция

x - порядкового типа

совпадает с типом параметра

возвращает предыдущее значение порядкового типа

Succ(x)

функция

x - порядкового типа

совпадает с типом параметра

возвращает следующее значение порядкового типа

Random(x)

функция

x - integer

integer

возвращает случайное целое в диапазоне от 0 до x-1

Random

функция

 

real

возвращает случайное вещественное в диапазоне [0..1)

Include(s,x)

процедура

s - множество, x - элемент, тип которого совместим с базовым типом множества

 

включает элемент x во множество s

Exclude(s,x)

процедура

s - множество, x - элемент, тип которого совместим с базовым типом множества

 

исключает элемент x из множества s

FillChar(v,count,x)

процедура

v - переменная любого типа, count - integer,
x
- byte

 

заполняет count значениями x область памяти, занимаемую переменной v. Контроль выхода за границы не производится

 

Стандартные процедуры и функции для работы со строками

Имя и параметры

Процедура или функция

Типы параметров

Тип возвращаемого значения

Действие

Length(s)

функция

s - string

integer

возвращает длину строки s

Copy(s,index,count)

функция

s - string, index и count - integer

string

возвращает подстроку строки s длины count, начиная с позиции index

Delete(s,index,count)

процедура

s - string, index и count - integer

 

удаляет в строке s count символов начиная с позиции index

Insert(subs,s,index)

процедура

s, subs - string, index - integer

 

вставляет подстроку subs в строку s с позиции index

Pos(subs,s)

функция

s, subs - string

integer

возвращает позицию первой подстроки subs в строке s (или 0 если подстрока не найдена)

SetLength(s,n)

процедура

s - string, n - integer

 

устанавливает длину строки s равной n

Str(x,s)
Str(x:n,s)
Str(x:n:m,s)

процедура

s - string, x - integer, real и n, m - integer

 

преобразует x к строковому представлению (во втором и третьем случаях согласно формату вывода, устанавливаемому n и m) и записывает результат в строку s

Val(s,v,code)

процедура

s - string, v - integer, real, и code - integer

 

преобразует строку s к числовому представлению и записывает результат в переменную v. Если преобразование возможно, то в переменной code возвращается 0, если невозможно, то в code возвращается ненулевое значение

IntToStr(i)

функция

i - integer

string

преобразует целое число к строке

StrToInt(s)

функция

s - string

integer

преобразует строку в целое число . Если преобразование невозможно, то возникает ошибка времени выполнения

FloatToStr(r)

функция

r - real

string

преобразует вещественное число к строке

StrToFloat(s)

функция

s - string

real

преобразует строку в вещественное число. Если преобразование невозможно, то возникает ошибка времени выполнения

UpCase(c)

функция

c - char

char

возвращает символ c, преобразованный к верхнему регистру

LowCase(c)

функция

c - char

char

возвращает символ c, преобразованный к нижнему регистру

UpperCase(s)

функция

s - string

string

возвращает строку s, преобразованную к верхнему регистру

LowerCase(s)

функция

s - string

string

возвращает строку s, преобразованную к нижнему регистру


Стандартные процедуры и функции для работы с файлами

Имя и параметры

Процедура или функция

Типы параметров

Тип возвращаемого значения

Действие

Assign(f,name)

процедура

f - переменная файлового типа , name - string

 

связывает файловую переменную f с файлом с именем name

Reset(f)

процедура

f - переменная файлового типа

 

открывает файл, ранее связанный с файловой переменной f с помощью процедуры Assign. Файл должен существовать на диске, в противном случае происходит ошибка времени выполнения. Текстовые файлы открываются только на чтение, типизированные файлы - на чтение и запись

Rewrite(f)

процедура

f - переменная файлового типа

 

создает и открывает новый файл, ранее связанный с файловой переменной f с помощью процедуры Assign. Если файл с указанным именем уже существует, то он удаляется, и вместо него создается новый файл. Текстовые файлы открываются только на запись, типизированные файлы - на чтение и запись

Append(f)

процедура

f - переменная типа Text

 

открывает текстовый файл на запись для добавления. Файловый указатель устанавливается в конец файла.

Close(f)

процедура

f - переменная файлового типа

 

закрывает файл

FileExists(name)

функция

name - string

boolean

возвращает True, если на диске имеется файл с именем name, в противном случае возвращает False

CanCreateFile(name)

функция

name - string

boolean

возвращает True, если можно создать файл с именем name, в противном случае возвращает False

Read(f,a,b,...)

процедура

f - переменная файлового типа, a,b - переменные простого типа, типа string или указатели

 

считывает значения из файла f в переменные a, b ... Если файл типизированный, то типы переменных a, b ... должны совпадать с базовым типом файла, а их значения считываются из файла в двоичном виде. Если файл текстовый, то переменные a, b ... могут иметь различные типы, а их значения должны храниться в файле в текстовом виде 

Write(f,a,b,...)

процедура

f - переменная файлового типа, a,b - выражения простого типа, типа string или указатели

 

записывает значения a, b ... в файл f. Если файл типизированный, то типы значений a, b ... должны быть совместимыми с базовым типом файла. Если файл текстовый, то значения a, b ... выводятся в него в текстовом виде, при этом могут быть использованы форматы вывода

Readln(f,a,b,...)

процедура

f - переменная типа Text, a,b - переменные простого типа, типа string или указатели

 

считывает значения из текстового файла f в переменные a, b ..., после чего пропускает символы до конца строки. Вызов readln(f) просто пропускает символы до конца строки

Writeln(f,a,b,...)

процедура

f - переменная типа Text, a,b - выражения простого типа, типа string или указатели

 

записывает значения a, b ... в текстовый файл f, после чего записывает в него символ конца строки. Значения a, b ... записываются в файл в текстовом виде, при этом могут быть использованы форматы вывода. Вызов writeln(f) просто записывает в файл символ конца строки

Eof(f)

функция

f - переменная файлового типа

boolean

возвращает True, если файловый указатель стоит на конце файла, и False в противном случае

Eoln(f)

функция

f - переменная типа Text

boolean

возвращает True, если файловый указатель стоит на конце строки, и False в противном случае

SeekEof(f)

функция

f - переменная типа Text

boolean

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

SeekEoln(f)

функция

f - переменная типа Text

boolean

пропускает пробелы, символы табуляции, после чего возвращает True, если файловый указатель стоит на конце строки, и False в противном случае

FileSize(f) 

функция

f - переменная типа file

integer

возвращает количество элементов в типизированном файле

FilePos(f)

функция

f - переменная типа file

integer

возвращает позицию файлового указателя в типизированном файле (нумерация элементов в типизированном файле начинается с нуля)

Seek(f,n)

процедура

f - переменная типа file

 

перемещает файловый указатель в типизированном файле на n-тый элемент (нумерация начинается с нуля)

Truncate(f)

процедура

f - переменная типа file

 

удаляет все элементы типизированного файла с текущей позиции файлового указателя до конца файла

Rename(f,name)

процедура

f - переменная файлового типа, name - string

 

переименовывает файл, связанный с файловой переменной f. Файл должен быть закрыт

Erase(f)

процедура

f - переменная файлового типа

 

удаляет файл, связанный с файловой переменной f. Файл должен быть закрыт

Стандартные процедуры и функции для работы с памятью

Имя и параметры

Процедура или функция

Типы параметров

Действие

New(p)

процедура

p - типизированный указатель

выделяет динамическую память размера, равного размеру типа, на который указывает p, и возвращает указатель на нее в переменной p

Dispose(p)

процедура

p - типизированный указатель

освобождает динамическую память по указателю p, ранее выделенную процедурой New

GetMem(p,n)

процедура

p - указатель любого типа, n - integer

выделяет динамическую память размера n байт и возвращает указатель на нее в переменной p

FreeMem(p)

процедура

p - указатель любого типа

освобождает динамическую память по указателю p, ранее выделенную процедурой GetMem

 

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

Имя и параметры

Процедура или функция

Типы параметров

Тип возвращаемого значения

Действие

Sleep(ms)

процедура

ms - integer

 

Осуществляет паузу в выполнении программы на ms миллисекунд

Cls

процедура

 

 

Очищает окно вывода

Halt(n)

процедура

n - integer

 

Завершает программу. Выводит в окно вывода предупреждение "Программа завершена вызовом Halt(n)"

 
 
 





 

 

Класи і об’єкти

 

Обзор классов и объектов

Класс представляет собой составной тип, состоящий из полей (переменных), методов (процедур и функций) и свойств.

Описание класса имеет вид:

type
  имя класса=class
    описания полей
          объявления или описания методов и описания свойств
  end;

После слова class в скобках может быть указано имя класса-предка (см. п. Наследование).

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

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

Например:

type
  Student=class
    name: string;
    course, group: integer;
    constructor Create(nm: string; c,gr: integer);
    procedure Print;
    begin
      writeln('Имя:',name,' курс:',course,' группа:');
    end;
    procedure NextCourse;
    begin
      Inc(course);
    end;
  end;

  constructor Student.Create(nm: string; c,gr: integer);
  begin
    name:=nm;
    course:=c;
    group:=g;
  end;

Здесь конструктор Create объявлен в классе, а определен вне класса, а методы Print и NextCourse определены внутри класса. При определении метода вне класса его имя предваряется именем класса с последующей точкой.

Обычно только небольшие методы определяются внутри класса. Это делается для того чтобы интерфейс класса был обозримым.

Объявление метода не может следовать за определением этого же метода.

Экземпляры классов называются объектами. Каждый объект содержит копии всех полей, определенных в его классе, и может пользоваться всеми его методами. Переменная типа класс хранит в действительности указатель на объект. Однако, при обращении к полям, методам или свойствам объекта разыменование такого указателя не требуется; указывается имя объекта и затем, после разделителя-точки, указывается имя поля, метода или свойства:

var s: Student;
...
s.name:='Иванов'; s.course:=1; s.group:=3;
s.Print;
s.GotoNextCourse;
s.Print;

Как и другие указатели, переменная типа класс может хранить значение nil:

s:=nil;
...
if s=nil then ...

Несколько переменных типа класс могут ссылаться на один объект:

var s1,s2: Student;
...
s1.name:=' Петров';
s2:=s1;
writeln(s2.name); // s2.name='Петров'

Локальные определения классов (т.е. определения в процедурах и функциях) запрещены.

В методах, описанных внутри класса запрещены вложенные описания процедур.



 Конструкторы и деструкторы

Объекты создаются и уничтожаются с помощью специальных методов, называемых конструкторами и деструкторами.

Конструктор представляет собой функцию, создающую объект в динамической памяти, инициализирующую его поля и возвращающую указатель на созданный объект. Этот указатель обычно сразу присваивается переменной типа класс. При описании конструктора вместо служебного слова function используется служебное слово constructor. Кроме того, для конструктора не указывается тип возвращаемого значения. Для вызова конструктора следует указать имя класса, за которым следует точка-разделитель, имя конструктора и список параметров. Например:

var s: Student;
...
s:=Student.Create('Иванов',1,3);

При создании объекта поля не инициализируются автоматически, поэтому рекомендуется все поля инициализировать в конструкторе явно.

Деструктор представляет собой процедуру, уничтожающую объект и освобождающую динамическую память, которую этот объект занимал. При описании деструктора вместо служебного слова procedure используется служебное слово destructor. Деструктор объекта вызывается как обычный метод:

s.Destroy;

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

s:=nil;

В Pascal ABC, как и в Borland Delphi, принято использовать имя Create для конструктора и имя Destroy - для деструктора.

В Pascal ABC конструктор можно также вызвать как обычную процедуру, при этом создание объекта не происходит, а выполняется лишь тело конструктора.

Свойства

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

property Prop: тип read имя функции чтения write имя процедуры записи; 

Как правило, каждое свойство связано с некоторым полем класса и возвращает значение этого поля с помощью функции чтения, а меняет - с помощью процедуры записи. Функция чтения и процедура записи должны быть методами этого класса и иметь следующий вид:

function getProp: тип;
procedure
setProp(v: тип);

Секция write может быть опущена, тогда доступ к полю через свойство можно осуществлять только на чтение (свойства только для чтения).

Например, рассмотрим тип Person (персона), имеющий поле _age (возраст). Доступ к полю _age будем осуществлять через свойство Age:

type Person=class
  _age: integer;
  procedure setAge(a: integer);
  begin
    if a>0 then _age:=a
    else writeln('Ошибка в Person.setAge: возраст не может быть отрицательным');
  end;
  function getAge: integer;
  begin
   
writeln('Осуществлен доступ к полю _age на чтение');
    Result:=_age;
  end;
  property Age: integer read getAge write setAge;
end;
var
  p: Person;
  a: integer;
...
p:=Person.Create;
p.Age:=-3; // выводится сообщение об ошибке
i:=p.Age; // выводится сообщение о чтении поля _age 

Всякий раз, когда мы присваиваем свойству Age новое значение , вызывается процедура setAge с соответствующим параметром. Всякий раз, когда мы считываем значение свойства Age, вызывается функция getAge.

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

p._age:=-3;

Присваивание полю _age отрицательного значения ошибочно, но подобная ошибка не будет обработана.

Вместо имени функции чтения или имени процедуры записи можно использовать имя поля, например:

property Age: integer read _age write setAge;

В этом случае строка i:=p.Age эквивалентна строке i:=p._age.

Обычно для доступа к полю на чтение в секции read свойства указывается именно поле, т.к. обычно при чтении поля никаких дополнительных действий производить не требуется.

Свойства не могут передаваться по ссылке в процедуры и функции. Например, следующий код ошибочен:

Inc(p.Age); // ошибка!

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

a:=p.Age;
Inc(a);
p.Age:=a;

Свойства очень удобны при работе с визуальными объектами, поскольку позволяют автоматически перерисовывать объект, если изменить какие-либо его визуальные характеристики. Например, если создана кнопка b1 типа Button, то для визуального изменения ее ширины достаточно присвоить значение ее свойству Width:

b1.Width:=100;

Процедура для записи этого свойства в приватное поле _width будет выглядеть примерно так:

procedure SetWidth(w: integer);
begin
  if (w>0) and (w<>_width) then
  begin
    _width:=w;
    <перерисовка кнопки>
  end
end;

Следует обратить внимание на вторую часть условия в операторе if: w<>_width. Добавление этой проверки позволяет избежать лишней перерисовки кнопки в случае, если ее ширина не меняется.

 

Индексные свойства

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

Индексное свойство описывается в классе следующим образом:

property Prop[имя: тип индекса]: тип read имя функции чтения write имя процедуры записи; 

Функция чтения и процедура записи должны быть методами этого класса и иметь следующий вид:

function getProp(ind: тип индекса): тип;
procedure
setProp(ind: тип индекса; v: тип);

Всякий раз, когда мы выполняем присваивание Prop[ind]:=value, вызывается процедура setProp(ind,value), а когда считываем значение Prop[ind], вызывается функция getProp(ind).

Первое индексное свойство, описанное в классе, называется индексным свойством по умолчанию и позволяет пользоваться объектами класса как массивами, т.е. использовать запись a[i], где a - объект класса.

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

type AssocArray=class
private
  words: StringArray; // слова
  nums: IntArray; // количество появлений слов
public
  constructor Create;
  begin
    words:=StringArray.Create;
    nums:=IntArray.Create;
  end;
  procedure setProp(ind: string; value: integer);
  function getProp(ind: string): integer;
  property Items[ind: string]: integer read getProp write setProp;
  property Keys: StringArray read words;
  property Values: IntArray read nums;
end;

procedure AssocArray.setProp(ind: string; value: integer);
var i: integer;
begin
  i:=words.IndexOf(ind); // поиск слова
  if i<>0 then           // если найдено, то
    nums[i]:=value       // установка количества появлений слова
  else                   // если не найдено, то
  begin                   
    words.add(ind);      // добавление слова в массив слов
    nums.add(value);     // установка количества появлений слова
  end;
end;

function AssocArray.getProp(ind: string): integer;
var i: integer;
begin
  i:=words.IndexOf(ind);  // поиск слова
  if i<>0 then            // если найдено, то
    Result:=nums[i]       // возвращение количества появлений слова
  else                    // если не найдено, то
  begin
    words.add(ind);       // добавление слова в массив слов
    nums.add(0);          // установка количества появлений слова равным нулю
    Result:=0;
  end;
end;
 

var CountNames: AssocArray;
...
CountNames.Items['крокодил']:=3;
CountNames['бегемот']:=5; // индексное свойство по умолчанию
CountNames['бегемот']:=CountNames['бегемот']+2;

Как и обычные свойства, индексные свойства не могут передаваться по ссылке в процедуры и функции. Так, в последнем примере нельзя написать

Inc(CountNames['бегемот'],2);

 

Переменная Self

Внутри каждого метода неявно определяется переменная Self, ссылающаяся на объект, вызвавший этот метод. Например:

type
  A=class
    i: integer;
    constructor Create(i: integer);
    begin
      Self.i:=i;
    end;
  end;

В момент вызова конструктора Create объект будет уже создан. Конструкция Self.i ссылается на поле i этого объекта, а не на параметр i функции Create.

Наследование

Класс может быть унаследован от другого класса. Например, при объявлении

type MyClass=class(BaseClass)
...
end;

класс MyClass наследуется от класса BaseClass.

Класс-потомок называется также производным классом или подклассом, а класс-предок - базовым классом или надклассом. Потомок автоматически наследует все поля и методы своего предка. Он может также определять новые поля и методы, а также переопределять (замещать) методы предка.

Имена полей и методов, объявленные в классе, доступны до конца объявления класса, а также во всех его потомках.

Все классы неявно наследуются от класса Object. Этот класс объявлен следующим образом:

type Object=class
  constructor Create;
  destructor Destroy;
  function TypeName: string;
  function ToString: string;
end;

Таким образом, любой класс может создавать свои объекты, вызывая конструктор Create без параметров, и уничтожать их, вызывая деструктор Destroy, даже если эти методы не приведены в его описании:

var m: MyClass;
...
m:=MyClass.Create;
...
m.Destroy;

В конце работы программы все созданные объекты должны быть разрушены. Поэтому для каждого созданного объекта рекомендуется явно вызвать деструктор. Однако, если этого не сделать, то система Pascal ABC после завершения программы автоматически вызывает деструкторы всех созданных объектов.

Метод TypeName возвращает имя класса объекта, а метод ToString - строковое представление объекта. В классе Object метод ToString также возвращает имя класса объекта, но это поведение может быть переопределено в потомках. Например:

type MyClass=class
  i: integer;
  constructor Create(i: integer);
  begin
    Self.i:=i;
  end;
  function ToString: string;
  begin
    Result:=IntToStr(i);
  end;
end
;
var a: MyClass;
...
a:=MyClass.Create(3);
writeln(a.TypeName); // MyClass
writeln(a.ToString); // 3 

Видимость членов класса

Каждое поле, метод или свойство класса имеет атрибут, называемый видимостью. В Pascal ABC существуют три типа атрибутов видимости: public (открытый), private (закрытый) и protected (защищенный). К члену класса, имеющему атрибут public, можно обратиться из любого места программы, члены класса с атрибутом private доступны только внутри методов этого класса, члены класса с атрибутом protected доступны внутри методов этого класса и всех его подклассов. Атрибуты видимости могут находиться внутри класса, при этом все дальнейшие члены приобретают этот атрибут. Например:

type
  A=class
  private
 
  x: integer;
  protected
    a: integer;
  public
    constructor
Create(xx: integer);
    begin
      x:=xx; // верно, т.к. мы внутри метода класса, которому принадлежит закрытое поле x
      a:=0; // верно
    end;
    procedure print;
  end;
  B=class(A)
    procedure print;
    begin
      writeln(a); // верно, т.к. a - защищенное поле
      writeln(x); // неверно, т.к. х - закрытое поле
    end;
  end;
...
var a1: A;
...
writeln(a1.x); // неверно, т.к. х - закрытое поле
writeln(a1.a); // неверно, т.к. a - защищенное поле
a1.print; // верно, т.к. print - открытый метод

По умолчанию все члены являются открытыми. В частности, метод print класса B - открытый.

Если x - закрытый или защищенный член класса, то доступ к нему с использованием явного указания объекта невозможен: запись a.x вызовет ошибку при компиляции "поле x недоступно". Исключение составляет запись Self.x, используемая в методе класса, содержащего x.

Переопределение методов

Метод базового класса может быть переопределен (замещен) в подклассах. Если при этом требуется вызвать метод базового класса, то используется служебное слово inherited (англ.- унаследованный). Например:

type
  Person=class
    name: string;
    age: integer;
    constructor Create(nm: string; ag: integer);
    begin
      name:=nm; age:=ag;
    end;
    procedure Print;
    begin
      writeln('Имя: ',name,'  Возраст: ',age);
    end;
  end;
  Student=class(Person)
    course, group: integer;
    constructor Create(nm: string; ag,c,gr: integer);
    begin
      inherited Create(nm,ag);
      course:=c; group:=gr;
      name:=nm; age:=ag;
    end;
    procedure Print;
    begin
      inherited Print;
      writeln('Курс: ',course,'  Группа: ',group);
    end;
  end;

Здесь метод Print производного класса Student вызывает вначале метод Print, унаследованный от базового класса Person, с помощью конструкции inherited Print. Аналогично конструктор Create класса Student вызывает вначале конструктор Create базового класса Person, также используя служебное слово inherited. Следует обратить внимание, что конструктор базового класса вызывается в этом случае как процедура, а не как функция, при этом создания нового объекта не происходит.

Приведение типов объектов

Объект производного класса неявно преобразуется к типу базового класса. Например, если у нас имеется класс

type MyClass=class
  procedure print;
end;

то можно инициализировать переменную класса Object объектом класса MyClass:

var o: Object;
...
o:=MyClass.Create;

Теперь в переменной типа Object хранится адрес объекта MyClass. Это можно проверить, вызвав функцию TypeName класса Object:

writeln(o.TypeName); // будет выведено 'MyClass'

Тип классовой переменной, заявленный при описании, принято называть ее статическим типом, а тип объекта, на который ссылается классовая переменная в процессе выполнения программы - ее динамическим типом. Динамический тип может либо совпадать со статическим, либо быть его наследником.

Объект базового класса можно явно преобразовать к типу производного класса. Так, в приведенном выше примере, хотя переменная o ссылается на объект типа MyClass, пользоваться методом print, определенным в классе MyClass, через переменную o нельзя. Чтобы это осуществить, объект базового класса должен быть явно преобразован к объекту производного класса:

var m: MyClass;
...
m=MyClass(o);

После этого, разумеется, можно вызывать любые методы производного класса и обращаться к его полям:

m.print;

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

Операции is и as

Операция is предназначена для проверки того, имеет ли классовая переменная указанный динамический тип. Операция as позволяет безопасно преобразовать переменную одного классового типа к другому классовому типу (в отличие от явного приведения классового типа).

Если a1 - переменная классового типа, а B - тип класса, то выражение a1 is B возвращает True если переменная a1 имеет динамический тип, совпадающий с B или являющийся одним из его потомков, и False в противном случае. Например:

type
  A=class
  end;

  B=class(A)
  end;
var
a1: A;
...
a1:=B.Create;
writeln(a1 is B); // True

Если a1 - переменная классового типа, а B - тип класса, то выражение a1 as B возвращает объект класса B, a1 имеет динамический тип B или производный от него и nil в противном случае. Например:

var b1: B;
...
b1:=a1 as B;

Фактически последняя строка делает то же, что и следующий фрагмент кода:

if a1 is B then
  b1:=B(a1)
else b1:=nil;

 Виртуальные методы и полиморфизм

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

type
  Base=class
    procedure Print;
    begin
      writeln('Base');
    end;
  end;
  Derived=class(Base)
    procedure Print;
    begin
      writeln('Derived');
    end;
  end;

Рассмотрим различные способы вызова метода Print:

var
  b,b1: Base;
  d: Student;
  ...
  b:=Base.Create;
  d:=Derived.Create;
  b1:=d;
  b.Print; // вызывается Base.Print
  d.Print; // вызывается Derived.Print
  b1.Print;

Какая версия метода Print вызывается в последнем случае - класса Base или класса Derived? В объектно-ориентированных языках программирования возможны две ситуации. Если решение о том, какой метод вызывать, принимается на этапе компиляции на основании типа, заявленного при описании b1, то вызывается метод Base.Print (говорят также, что имеет место раннее связывание имени метода с его телом). Если же решение о том, какой метод вызывать, принимается на этапе выполнения программы в зависимости от реального типа объекта, на который ссылается переменная b1, то вызывается метод Derived.Print (говорят также, что имеет место позднее связывание). Методы, для которых реализуется позднее связывание, называются виртуальными, а способность объекта вызывать через переменную базового класса метод производного класса на основании информации во время выполнения программы называется полиморфизмом.

В языке Pascal ABC все методы (за исключением конструкторов) являются виртуальными, т.е. в последнем случае вызовется функция Derived.Print.

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

type
  Shape=class
    procedure Draw; begin end;
  end;
  Point=class
    procedure Draw; begin ... end;
  end;
  Rectangle=class
    procedure Draw; begin ... end;
  end;
  Circle=class
    procedure Draw; begin ... end;
  end;
var a: array[1..4] of Shape;
...
  a[1]:=Point.Create;
  a[2]:=Rectangle.Create;
  a[3]:=Point.Create;
  a[4]:=Circle.Create;
  for i:=1 to 4 do
    a[i].Draw;

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

Внимание! Если переопределяется private-метод, то цепочка виртуальности ломается, т.е. метод рассматривается как первый с этим именем в новой цепочке виртуальности.

 

 

Модулі