Практика
Эффективное использование процедур и функций

:: Меню ::
:: На главную ::
:: FAQ ::
:: Заметки ::
:: Практика ::
:: Win API ::
:: Проекты ::
:: Скачать ::
:: Секреты ::
:: Ссылки ::

:: Сервис ::
:: Написать ::

:: MVP ::

:: RSS ::

Яндекс.Метрика

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

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

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

Form1.Show;
Form1.Show();

Возможность перегрузки

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

procedure Test( I: integer ); overload;
procedure Test( S: string ); overload;
procedure Test( D: double ); overload;

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

Передача параметров

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

Передача параметров по значению

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

procedure Test( s: string );

При вызове указанной процедуры будет создана копия передаваемой ей в качестве параметра строки s, с которой и будет работать процедура Test. При этом все внесенные в строку изменения никак не отразятся на исходной переменной s.

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

Передача параметров по ссылке

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

procedure ChangeMe( var x: longint );
begin
   x := 2; // Параметр х изменен зызванной процедурой
end;

Вместо создания копии переменной x, ключевое слово var требует передачи адреса самой переменной x, что позволяет процедуре непосредственно изменять ее значение.

Передача параметров констант

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

procedure Test( const s: string );

Передача открытых массивов

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

function AddEmUp( A: array of integer ): integer;

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

const
  j = 23;
var
  i, Rez: integer;
begin
   i := 8;
   Rez := AddEmUp( [i, 20, j, 43] );
end;

Для получения информации о фактически передаваемом массиве параметров в функции или процедуре могут использоваться функции High, Low и SizeOf. Для иллюстрации их использования ниже приведен текст функции AddEmUp, которая возвращает сумму всех переданных ей элементов массива A.

function AddEmUp( A: array of integer ): integer;
var
  i: integer;
begin
   Result := 0;
   for i := Low(A) to High(A) do
      Inc( Result, A[i] );
end;

Object Pascal также поддерживает тип array of const, который позволяет передавать в одном массиве данные различных типов. Синтаксис объявления функций или процедур, использующих такой массив для получения параметров, следующий:

procedure WhatHaveIGot( A: array of const );

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

procedure WhatHaveIGot( ['Text', 10, 5.5, @WhatHaveIGot, 3.14, true, 'c'] );

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

PVarRec = ^TVarRec;
TVarRec = record
  case Byte of
    vtInteger:    (VInteger: Integer; VType: Byte);
    vtBoolean:    (VBoolean: Boolean);
    vtChar:       (VChar: Char);
    vtExtended:   (VExtended: PExtended);
    vtString:     (VString: PShortString);
    vtPointer:    (VPointer: Pointer);
    vtPChar:      (VPChar: PChar);
    vtObject:     (VObject: TObject);
    vtClass:      (VClass: TClass);
    vtWideChar:   (VWideChar: WideChar);
    vtPWideChar:  (VPWideChar: PWideChar);
    vtAnsiString: (VAnsiString: Pointer);
    vtCurrency:   (VCurrency: PCurrency);
    vtVariant:    (VVariant: PVariant);
    vtInterface:  (VInterface: Pointer);
    vtWideString: (VWideString: Pointer);
    vtInt64:      (VInt64: PInt64);
end;

Поле VType определяет тип содержащихся в данном экземпляре записи TVarRec данных и может принимать одно из ниже приведенных значений.

const
  vtInteger    = 0;
  vtBoolean    = 1;
  vtChar       = 2;
  vtExtended   = 3;
  vtString     = 4;
  vtPointer    = 5;
  vtPChar      = 6;
  vtObject     = 7;
  vtClass      = 8;
  vtWideChar   = 9;
  vtPWideChar  = 10;
  vtAnsiString = 11;
  vtCurrency   = 12;
  vtVariant    = 13;
  vtInterface  = 14;
  vtWideString = 15;
  vtInt64      = 16;

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

procedure WhatHaveIGot( A: array of const );
var
  i: integer;
  TypeStr: string;
begin
   for i := Low(A) to High(A) do
   begin
      case A[i].VType of
         vtInteger    : TypeStr := 'Integer';
         vtBoolean    : TypeStr := 'Boolean';
         vtChar       : TypeStr := 'Char';
         vtExtended   : TypeStr := 'Extended';
         vtString     : TypeStr := 'String';
         vtPointer    : TypeStr := 'Pointer';
         vtPChar      : TypeStr := 'PChar';
         vtObject     : TypeStr := 'Object';
         vtClass      : TypeStr := 'Class';
         vtWideChar   : TypeStr := 'WideChar';
         vtPWideChar  : TypeStr := 'PWideChar';
         vtAnsiString : TypeStr := 'AnsiString';
         vtCurrency   : TypeStr := 'Currency';
         vtVariant    : TypeStr := 'Variant';
         vtInterface  : TypeStr := 'Interface';
         vtWideString : TypeStr := 'WideString';
         vtInt64      : TypeStr := 'Int64';
      end;
      ShowMessage( Format( 'Array item %d is a %s', [i, TypeStr] ) );
   end;
end;

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

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

procedure HasDefVal( s: string; i: integer = 0 );

Подобное объявление означает, что процедура HasDefVal может быть вызвана двумя путями. В первом случае - как обычно, с указанием обоих параметров:

procedure HasDefVal( 'Hello', 26 );

Во втором случае можно задать только значение параметра s, а для параметра i использовать значение, установленное по умолчанию:

procedure HasDefVal( 'Hello' );

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

function Add( I1, I2: integer ): integer;
begin
   Result := I1 + I2;
end;

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

function Add( I1, I2: integer; I3: integer = 0 ): integer;
begin
   Result := I1 + I2 + I3;
end;

Директива {$X-}

Директива {$X-} запрещает вызов функций как процедур (с игнорированием возвращаемого результата). По умолчанию этот режим включен ({$X+}). Так вот, запомните, использование переменной Result недопустимо при сброшенном флажке опции Extended Syntax, расположенном во вкладке Compiler диалогового окна Project Options, или при указании директивы компилятора {$X-}.

В каждой функции языка Objecl Pascal существует локальная переменная с именем Result, предназначенная для размещения возвращаемого значения. Кроме того, вернуть значение из функции можно также путем присвоения значения переменной, имеющей то же имя, что и данная функция. Это стандартный синтаксис языка Pascal, сохранившийся от его предыдущих версий. При использовании в теле функции переменной с ее именем не забывайте, что существуют большие отличия в обработке этого имени - все зависит от того, где она расположена - в левой части оператора присвоения или же в любом другом месте текста функции. Если имя функции указано в левой части оператора присвоения, то предполагается, что назначается возвращаемое функцией значение. Во всех других случаях предполагается, что осуществляется рекурсивный вызов этой функции.

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


При использовании материала - ссылка на сайт обязательна