Библиотека программиста

«Любой дурак может написать программу, которую поймет компилятор. Хорошие программисты пишут программы, которые смогут понять другие программисты»

Мартин Фаулер

Главная страница > Язык Object Pascal > 20. Параметры подпрограмм

20. Параметры подпрограмм

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

Формальные параметры можно классифицировать на следующие виды: параметры значения, переменные, константы и выходные (var, const и out).

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

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

Параметры переменные и параметры значения.

Параметры значения передаются как значения, в то время как параметры переменные передаются по ссылке (reference). Для пояснения различия между ними сравните две функции:

function DoubleByValue(X: Integer): Integer; // X is a value parameter

begin

X := X * 2; Result := X;

end;

function DoubleByRef(var X: Integer): Integer; // X is a variable parameter

begin

X := X * 2; Result := X;

end;

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

var I, J, V, W: Integer;

begin

I := 4; V := 4;

J := DoubleByValue(I); // J = 8, I = 4

W := DoubleByRef(V); // W = 8, V = 8

end;

Даже если одна и та же переменная используется как два или более параметров, никаких копий не создается, например:

procedure AddOne(var X, Y: Integer);

begin

X := X + 1; Y := Y + 1;

end;

var I: Integer;

begin

I := 1;

AddOne(I, I); // i=3

end;

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

Параметры константы.

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

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

Выходные параметры.

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

procedure GetInfo(out Info: SomeRecordType);

При вызове этой процедуры ей передается некоторая переменная типа SomeRecordType и она не используется для передачи каких-либо данных процедуре. Выходные параметры часто используются при использовании COM и CORBA моделей. Кроме того, параметр необходимо объявлять выходным, если процедуре передается неинициализированная переменная.

Нетипизированные параметры.

Любой из параметров – var, const или out – может быть объявлен как не имеющий типа, т.е. нетипизированный. Исключение составляет лишь параметр значение, тип которого должен быть указан обязательно. Пример:

procedure TakeAnything(var A; const B; out C);

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

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

Function Equal(var Source, Dest; Size: Integer): Boolean;

type

TBytes = array[0..MaxInt - 1] of Byte;

var

N: Integer;

Begin

N := 0;

while (N < Size) and (TBytes(Dest)[N] = TBytes(Source)[N]) do Inc(N);

Equal := N = Size;

End;

Теперь при наличии описаний

type

TVector = array[1..10] of Integer;

TPoint = record

X, Y: Integer;

end;

var

Vec1, Vec2: TVector;

N: Integer;

P: TPoint;

допустимы такие вызовы функции Equal:

Equal(Vec1, Vec2, SizeOf(TVector)) // compare Vec1 to Vec2

Equal(Vec1, Vec2, SizeOf(Integer) * N) // compare first N elements of Vec1 and Vec2

Equal(Vec1[1], Vec1[6], SizeOf(Integer) * 5) // compare first 5 to last 5 elements of Vec1

Equal(Vec1[1], P, 4) // compare Vec1[1] to P.X and Vec1[2] to P.Y

Строковые параметры.

При передаче строковых параметров рекомендуется использовать длинные строки вместо коротких строк (Shortstring) и открытых строк (OpenString). При директивах компилятора {$H–} и {$P+} тип string эквивалентен типу OpenString в описании параметров и такая возможность оставлена только для обратной совместимости. (Директива Н+ включает трактовку типа string как типа AnsiString.)

Параметры открытые массивы (open arrays).

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

Например, описание

function Find(A: array of Char): Integer;

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

type TDynamicCharArray = array of Char;

function Find(A: TDynamicCharArray): Integer;

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

Ø открытые массивы всегда индексируются с нуля (zero-based array). Для такого массива функция Low всегда возвращает 0, а High– число элементов массива минус 1. Функция SizeOfвозвращает размер массива, переданного ей как фактический параметр;

Ø доступ возможен только к отдельным элементам массива, но не к массиву целиком (имеется в виду присваивание);

Ø внутри подпрограммы открытые массивы могут передаваться в другие подпрограммы только как открытые массивы или нетипизированные параметры;

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

Когда открытый массив описан как параметр значение, компилятор создает массив-копию в стеке подпрограммы. Примеры:

procedure Clear(var A: array of Real);

var

I: Integer;

begin

for I := 0 to High(A)-1 do A[I] := 0;

end;

function Sum(const A: array of Real): Real;

var

I: Integer;

begin

Result := A[0];

for I := 1 to High(A)-1 do Result := Result + A[I];

end;

Вариантный массив как параметр открытый массив.

Объявление параметра как вариантного массива позволяет передавать в подпрограмму массивы, состоящие из элементов разного типа. Такие массивы называются гетерогенными (heterogeneous), т.е. неоднородными.

Для описания подпрограммы с параметром вариантный массив надо тип параметра описать как array of const, например

procedure DoSomething(A: array of const);

Описание array of const эквивалентно описанию array of TVarRec (которое тоже можно использовать). Тип TVarRec, описанный в модуле System, представляет собой запись с вариантной частью, которая может содержать значения таких типов, integer, Boolean, character, real, string, pointer, class, class reference, interface и variant. Поле VType этой записи указывает на тип каждого элемента массива.

Некоторые типы передаются как указатели, а не как значения. Например, длинные строки передаются как указатели (Pointer) и их нужно приводить к типу string.

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

Function MakeStr(const Args: array of const): string;

const

BoolChars: array[Boolean] of Char = ('F', 'T');

var

I: Integer;

Begin

Result := '';

for I := 0 to High(Args) do

with Args[I] do

case VType of

vtInteger: Result := Result + IntToStr(VInteger);

vtBoolean: Result := Result + BoolChars[VBoolean];

vtChar: Result := Result + VChar;

vtExtended: Result := Result + FloatToStr(VExtended^);

vtString: Result := Result + VString^;

vtPChar: Result := Result + VPChar;

vtObject: Result := Result + VObject.ClassName;

vtClass: Result := Result + VClass.ClassName;

vtAnsiString: Result := Result + string(VAnsiString);

vtCurrency: Result := Result + CurrToStr(VCurrency^);

vtVariant: Result := Result + string(VVariant^);

vtInt64: Result := Result + IntToStr(VInt64^);

end;

End;

Идентификаторы VInteger, VBoolean, VChar и др. представляют собой имена полей записи TVarRec. Теперь мы можем вызвать функцию MakeStr, использовав, например, конструктор вариантного массива в качестве фактического параметра:

MakeStr(['test', 100, ' ', True, 3.14159, TForm])

Это выражение вернет строку “test100 T3.14159TForm”.

Параметры по умолчанию (default parameters).

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

Например, если объявить процедуру

procedure FillArray(A: array of Integer; Value: Integer = 0);

то тогда следующие ее вызовы будут эквивалентными:

FillArray(MyArray);

FillArray(MyArray, 0);

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

function MyFunction(X, Y: Real = 3.5): Real; // syntax error

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

Значения по умолчанию могут быть указаны и при объявлении процедурного типа. В этом случае они перекрывают те значения по умолчанию, которые могут быть указаны в заголовке конкретной подпрограммы. Так, при таких описаниях

type TResizer = function(X: Real; Y: Real = 1.0): Real;

function Resizer(X: Real; Y: Real = 2.0): Real;

Begin … End;

var

F: TResizer;

N: Real;

операторы

F := Resizer;

F(N);

эквивалентны вызову функции с параметрами (N, 1.0).

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

Параметры по умолчанию и перегружаемые подпрограммы.

При использовании параметров по умолчанию в перегружаемых подпрограммах нужно избегать неоднозначности интерпретации списка фактических параметров (ambiguous parameter signatures). Сравните, например, такие процедуры:

procedure Confused(I: Integer); overload;

. . .

procedure Confused(I: Integer; J: Integer = 0); overload;

. . .

Confused(X); //Какая из процедур должна вызываться?

Такой код вызовет ошибку компилятора.

Параметры по умолчанию при объявлении подпрограмм в интерфейсной секции и с директивой forward.

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





<< Предыдущая статья
«19. Перегрузка подпрограмм»
Следующая статья >>
21. Вызовы подпрограмм