понедельник, 11 апреля 2011 г.

Параметры

Перевод из справочной системы Delphi

О параметрах

Большинство заголовков процедур и функций имеют список параметров. Например, в заголовке:

function Power(X: Real; Y: Integer): Real;
Список параметров – это (X: Real; Y: Integer).

Список параметров – это последовательность объявлений параметров, разделяемых точками с запятой (;) и заключенных в круглые скобки. Каждое объявление представляет собой последовательность разделенных запятыми имен параметров, за которой в большей части случаев следует двоеточие и идентификатор типа (в некоторых случаях далее может следовать знак равенства (=) и значение по умолчанию. Имена параметров должны быть допустимыми идентификаторами. Любое объявление может предваряться ключевыми словами var, const или out. Примеры:

(X, Y: Real)
(var S: string; X: Integer)
(HWnd: Integer; Text, Caption: PChar; Flags: Integer)
(const P; I: Integer)

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

procedure UpdateRecords;
begin
  ...
end;

В теле процедуры или функции имена параметров ( X и Y в первом примере) могут быть использованы как локальные переменные. Не следует переопределять имена параметров в секции локальных объявлений в теле процедуры или функции.

Семантика параметров

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

  • Каждый параметр классифицируется как параметр-значение (value parameter), параметр-переменная(variable parameter), параметр-константа (constant parameter) или выходной параметр (out parameter). По умолчанию параметры классифицируются как параметры-значения. Зарезервированные слова var, const и out обозначают параметры-переменные, параметеры-константы и выходные параметры соответсвенно.
  • Параметры-значения всегда типизированы, а параметры-константы, параметры-переменные и выходные параметры могут быть как типизированными, так и нетипизированными.
  • Особые правила применяются к массивам параметров

Файлы и экземпляры структурированных типов, содержащие файлы могут передаваться только как параметры-переменные (var).


Передача параметров по ссылке и по значению

Большинство параметров – это параметры-значения (по умолчанию) или параметры-переменные (var). Параметры-значения передаются по значению, а параметры-переменные передаются по ссылке. Чтобы понять, что это обозначает, рассмотрим следующие функции:

function DoubleByValue(X: Integer): Integer;   
// X - параметр value
begin
  X := X * 2;
  Result := X;
end;

function DoubleByRef(var X: Integer): Integer;  
// X – параметр variable 
begin
  X := X * 2;
  Result := X;
end;

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

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;

После выполнение этого кода, переменная I, которая передана в DoubleByValue, имеет такое же значение, как ей было присвоено первоначально. А переменная V, которую мы передали в DoubleByRef, значение изменила.

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

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

Копирования не происходит даже если одна переменная передается в два или более параметров-переменных. Это может быть проиллюстрировано следующим примером:

procedure AddOne(var X, Y: Integer);
begin
  X := X + 1;
  Y := Y + 1;
end;

var I: Integer;
begin
  I := 1;
  AddOne(I, I);
end;
После выполнения этого кода значение переменной I равно 3.

Если объявление подпрограммы определяет параметр-переменную, при вызове подпрограммы вы должны передать в качестве параметра выражение, которому может быть присвоено значение, то есть переменную, типизированную константу (при задействовании директивы {$J+}), ссылку указателя, поле или индексированную переменную. В нашем предыдущем примере вызов DoubleByRef(7) вызовет ошибку, а DoubleByValue(7) – будет правильным.

Индексы и ссылки указателей, переданные в параметры-переменные (как, например в вызове DoubleByRef(MyArray[I])) – вычисляются единожды перед выполнением подпрограммы.

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

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

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

Далее приведен заголовок функции CompareStr в модуле SysUtils:

function CompareStr(const S1, S2: string): Integer;

Поскольку S1 и S2 не изменяются в теле CompareStr, они могут быть объявлены как параметры-константы.

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

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

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

procedure GetInfo(out Info: SomeRecordType);
При вызове процедуры GetInfo вы должны передать ей переменную типа SomeRecordType:

var MyRecord: SomeRecordType;
   ...
GetInfo(MyRecord);

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

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

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

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

procedure TakeAnything(const C);

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

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

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

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));
// сравнивает Vec1 и Vec2
Equal(Vec1, Vec2, SizeOf(Integer) * N);  
// сравнивает первые N элементов Vec1 и Vec2
Equal(Vec1[1], Vec1[6], SizeOf(Integer) * 5); 
// сравнивает первые 5 и последние 5 элементов Vec1
Equal(Vec1[1], P, 4); 
// сравнивает Vec1[1] с P.X и Vec1[2] с P.Y

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

Когда вы объявляете подпрограммы, которые принимают параметры типа shortstring, вы не можете указывать длину строк в объявлении параметров. То есть следующее объявление вызывает ошибку компиляции:

procedure Check(S: string[20]);   // синтаксическая ошибка
Правильным будет следующее объявление:
type TString20 = string[20];
procedure Check(S: TString20);

Специализированный идентификатор OpenString может быть использован для объявления подпрограмм, принимающих параметры типа shortstring различной длины:

procedure Check(S: OpenString);

Когда одновременно действуют директивы компилятора {$H} и {$P+}, зарезервированное слово string при объявлении параметров эквивалентно OpenString.

Тип данных Shortstring, OpenString, директивы $H и $P поддерживаются только для обратной совместимости. В новом коде вы можете пользоваться типом данных string, не рассматривая предыдущий материал.

Параметры-массивы

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

procedure Sort(A: array[1..10] of Integer)
// синтаксическая ошибка
вызывает ошибку компиляции. Но объявление:
type TDigits = array[1..10] of Integer;
procedure Sort(A: TDigits);

является правильным. Еще одним решением является использование в качестве параметров динамических массивов.

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

type
  TDynamicArray = array of Integer;
  procedure p(Value: TDynamicArray);
    begin
      Value[0] := 1;
    end;

  procedure Run;
    var
      a: TDynamicArray;
    begin
      SetLength(a, 1);
      a[0] := 0;
      p(a);
      Writeln(a[0]); // Prints '1'
    end;

Следует учесть, что присваивание значения Value[0] в подпрограме p изменит содержимое динамического массива в вызывающем блоке, несмотря на то, что Value передается как параметр-значение. Если требуется создание полной копии динамического массива, следует пользоваться стандартной процедурой Copy.

Параметры-открытые массивы

Параметры-открытые массивы позволяют передавать в качестве параметров в одну и ту же процедуру или функцию массивы различных размеров. Чтобы объявить подпрограмму с параметром-открытым массивом, следует в списке параметров использовать синтаксис array of type (вместо array[X..Y] of type). Например:

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

объявляет функцию Find, которая в качестве параметра принимает массив символов произвольной длины и возвращает значение типа integer.

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

type TDynamicCharArray = array of Char;
function Find(A: TDynamicCharArray): Integer;
В теле подпрограммы открытые массивы обрабатываются по следующим правилам:
  • Они всегда индексируются от нуля. Первый элемент - 0, второй – 1, и так далее. Стандартные функции Low и High возвращают 0 и Length -1 соответственно. Функция SizeOf возвращает размер массива, переданного подпрограмме.
  • Доступ к массиву выполняется только через его элементы. Присваивание значения целому массиву не разрешено.
  • Они могут быть переданы в другую процедуру или функцию только как параметры-открытые массивы или как нетипизированные параметры-переменные. Кроме того, они не могут передаваться в качестве параметра в процедуру SetLength.
  • Вместо массива в подпрограмму можно передать переменную, совпадающую по типу с базовым типом массива. Она будет обрабатываться как массив с одним элементом.

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

Следующие примеры используют параметры-открытые массивы для определения процедуры Clear, которая присваивает всем элементам массива вещественных чисел значение 0 и функции Sum которая вычисляет сумму всех элементов массива вещественных чисел:

procedure Clear(var A: array of Real);
var
   I: Integer;
begin
   for I := 0 to High(A) do A[I] := 0;
end;

function Sum(const A: array of Real): Real;
var
  I: Integer;
  S: Real;
begin
  S := 0;
  for I := 0 to High(A) do S := S + A[I];
  Sum := S;
end;

При вызове этих подпрограмм вы можете передавать им в качестве параметров конструкторы открытых массивов.

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

Вариантные параметры-открытые массивы позволяют передавать в процедуру или функцию массивы с различными базовыми типами. Для объявления подпрограммы с вариантным параметром-открытым массивом следует для параметра указать тип array of const. То есть:

procedure DoSomething(A: array of const);
объявляет процедуру DoSomething, которая может работать с гетерогенными массивами.

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

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

function MakeStr(const Args: array of const): string;
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 + BoolToStr(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);
          vtUnicodeString:  
    Result := Result + string(VUnicodeString);
          vtCurrency:    
    Result := Result + CurrToStr(VCurrency^);
          vtVariant:     
    Result := Result + string(VVariant^);
          vtInt64:       
    Result := Result + IntToStr(VInt64^);
  end;
end;
Эту функцию можно вызывать, используя конструктор открытого массива. Например:
MakeStr(['test', 100, ' ', True, 3.14159, TForm])
Вернет строку 'test100 T3.14159TForm'.

Значения параметров по умолчанию

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

procedure FillArray(A: array of Integer; Value: Integer = 0);
Следующие вызовы процедуры будут эквивалентны:
FillArray(MyArray);
FillArray(MyArray, 0);

При множественном объявлении параметров указывать значения по умолчанию нельзя. То есть правильным будет объявление вида:

function MyFunction(X: Real = 3.5; Y: Real = 3.5): Real;
А следующее объявление - ошибочно:
function MyFunction(X, Y: Real = 3.5): Real;  
// синтаксическая ошибка

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

procedure MyProcedure(I: Integer = 1; S: string);  
// syntax error

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

type TResizer = function(X: Real; Y: Real = 1.0): Real;
function Resizer(X: Real; Y: Real = 2.0): Real;
var
  F: TResizer;
  N: Real;
При выполнении инструкций:
F := Resizer;
F(N);
В функцию Resizer будут переданы значения (N, 1.0).

Значения по умолчанию ограничены значениями, которые могут быть определены как константное выражение. Следовательно, параметры – динамические массивы, параметры процедурного типа, классы, метаклассы, интерфейсы не могут иметь значения по умолчанию, отличного от nil. Параметры типа record, variant, статические массивы, экземпляры классов вообще не могут иметь значений по умолчанию.

Значения параметров по умолчанию и перегружаемые функции

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

procedure Confused(I: Integer); overload;
   ...
procedure Confused(I: Integer; J: Integer = 0); overload;
   ...
Confused(X);   //  Какая процедура вызывается?

Фактически ни одна из процедур не может быть вызвана. Этот код вызовет ошибку компиляции.

Значения параметров по умолчанию в объявлениях Forward и Interface

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

Комментариев нет:

Отправить комментарий