Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

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
.

Если этот код пройти по шагам в отладчике, то никакого зацикливания не возникает:

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

Причина этой проблемы в том, как VCL обрабатывает сообщения, которые система помещает в очередь. При нажатии на кнопку компонента

TUpDown
в очередь сообщений помещаются два сообщения:
WM_LBUTTONDOWN
и
WM_NOTIFY
. Компонент
TUpDown
по умолчанию имеет стиль
csCaptureMouse
 — это означает, что при обработке
WM_LBUTTONDOWN
VCL захватывает мышь в монопольное пользование для данного компонента.

Примечание

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

Поделиться:
Популярные книги

Как я строил магическую империю 4

Зубов Константин
4. Как я строил магическую империю
Фантастика:
боевая фантастика
постапокалипсис
аниме
фантастика: прочее
фэнтези
5.00
рейтинг книги
Как я строил магическую империю 4

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Попаданка 3

Ахминеева Нина
3. Двойная звезда
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Попаданка 3

Муж на сдачу

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Муж на сдачу

Призыватель нулевого ранга. Том 3

Дубов Дмитрий
3. Эпоха Гардара
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Призыватель нулевого ранга. Том 3

На границе империй. Том 10. Часть 5

INDIGO
23. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 5

Адвокат

Константинов Андрей Дмитриевич
1. Бандитский Петербург
Детективы:
боевики
8.00
рейтинг книги
Адвокат

На границе империй. Том 7

INDIGO
7. Фортуна дама переменчивая
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
6.75
рейтинг книги
На границе империй. Том 7

Здравствуй, 1985-й

Иванов Дмитрий
2. Девяностые
Фантастика:
альтернативная история
5.25
рейтинг книги
Здравствуй, 1985-й

О, Путник!

Арбеков Александр Анатольевич
1. Квинтет. Миры
Фантастика:
социально-философская фантастика
5.00
рейтинг книги
О, Путник!

Чужбина

Седой Василий
2. Дворянская кровь
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Чужбина

Бестужев. Служба Государевой Безопасности. Книга четвертая

Измайлов Сергей
4. Граф Бестужев
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга четвертая

Локки 5. Потомок бога

Решетов Евгений Валерьевич
5. Локки
Фантастика:
юмористическое фэнтези
аниме
фэнтези
5.00
рейтинг книги
Локки 5. Потомок бога

На границе империй. Том 10. Часть 4

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 4