var
A1, A2: Extended;
begin
X := 2;
A1 := X / GetValueAndModifyX;
X := 2;
A2 := GetValueAndModifyX / X;
Label1.Caption := FloatToStr(A1);
Label2.Caption := FloatToStr(A2);
end;
В результате выполнения этого кода A1
получает значение 0.5, A2
— 2, т.е. и здесь сначала вычисляется функция, а потом берется значение переменной X
.
Если бы функция GetValueAndModifyX
не имела побочных эффектов (т.е. только возвращала бы результат и больше ничего не меняла), порядок вычисления аргументов был бы нам безразличен. Вообще, функции, имеющие побочные эффекты, считаются потенциальным источником ошибок, поэтому их написание нежелательно. Но в некоторых случаях (например, в функции Random
) обойтись без побочных эффектом невозможно.
Примечание
Побочные эффекты в функциях настолько небезопасны, что в некоторых языках они полностью запрещены. Например, в Аде изменять значения глобальных переменных могут только процедуры, но не функции.
Ради интереса посмотрим, что будет, если вторым аргументом тоже будет функция, зависящая от X
, (листинг 3.49).
Листинг 3.49. Сложение двух операндов с побочными эффектами
function GetX: Integer;
begin
Result := X;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
A1, A2: Integer;
begin
X:= 2;
A1 := GetX + GetValueAndModifyX;
X := 2;
A2 := GetValueAndModifyX + GetX;
Label1.Caption := IntToStr(A1);
Label2.Caption := IntToStr(A2);
end;
Здесь A1
получит значение 4, A2
— 3, т.e. интуитивно ожидаемые. Тем не менее полагаться на интуицию все же не стоит: в более сложных случаях она может подвести. Дело в том, что стандарт языка Паскаль разрешает разработчикам конкретной реализации языка самим выбирать порядок вычисления операндов [5]. Поэтому, даже если вам удалось добиться желаемого порядка вычисления, в следующих версиях Delphi (или при переносе на другую платформу) программа может начать работать неправильно. Таким образом, разработчик не имеет права делать какие-то предположения о том, в каком порядке будут вычисляться операнды, а когда изменение этого порядка может повлиять на результат, код должен быть написан таким образом, чтобы исключить эту возможность. В частности, пример со сложением должен быть переписан так (листинг 3.50).
Листинг 3.50. Явное управление порядком вычисления операндов
procedure TForm1.Button1Click(Sender: TObject);
var
A1, A2: Integer;
begin
X := 2;
A1 := X;
Inc(A1, GetValueAndModifyX);
X := 2;
A2 := GetValueAndModifyX;
Inc(A2, X);
Label1.Caption := IntToStr(A1);
Label2.Caption := IntToStr(A2);
end;
Такой код, несмотря на побочные эффекты функции GetValueAndModifyX
, даст ожидаемые значения при любом порядке вычисления операндов, т.к. здесь вычисление операндов разнесено по разным операторам, а порядок выполнения операторов четко определен.
Примечание
Другие компиляторы могут использовать иной порядок вычисления операндов. Так, FreePascal вычисляет их в том порядке, в каком они встречаются в выражении, т.е. в первом примере А1
получит значение 4, А2
— 3.
3.4.2. Зацикливание обработчика TUpDown.OnClick при открытии диалогового окна в обработчике
Для демонстрации этого "подводного камня" нам потребуется проект, на форме которого находится компонент TUpDown
со следующим обработчиком события OnClick
(листинг 3.51, пример UpDownDlg на компакт-диске).
Листинг 3.51. Обработчик события OnClick
компонента UpDown1
procedure TForm1.UpDown1Click(Sender: TObject; Button: TUDBtnType);
begin
Application.MessageBox('Text', 'Caption', MB_OK);
end;
Теперь, если запустить программу и нажать на верхнюю кнопку UpDown1
, откроется окно с сообщением (при нажатии на нижнюю кнопку окно не будет открываться потому, что по умолчанию у компонент TUpDown
свойства Position
и Min
равны нулю, поэтому нажатие на нижнюю кнопку не приводит к изменению значения Position
, и событие OnClick
не возникает; если изменить значение свойства Min
или Position
, то тот же эффект будет наблюдаться и при нажатии на нижнюю кнопку). Если закрыть это окно, то щелчок мышью в любом месте формы снова приведет к срабатыванию события OnClick
и открытию окна, и так до бесконечности: любой щелчок по форме в любом ее месте будет снова и снова приводить к появлению сообщения. Эффект наблюдается и в том случае, когда вместо стандартного сообщения в обработчике показывается любая другая модальная форма. Кроме того, тот же эффект будет, и если использовать события OnChanging
или OnChangingEx
вместо OnClick
, но мы далее для определенности будем говорить только об OnClick
.
Читать дальше
Конец ознакомительного отрывка
Купить книгу