Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Ракитин Р.Ю. ООП в Turbo Delphi

.PDF
Скачиваний:
55
Добавлен:
18.03.2015
Размер:
3.59 Mб
Скачать

71

Глава 5. Понятие исключительной ситуации и её обработка

Успешное завершение процесса компиляции не означает, что в программе нет ошибок. Убедиться, что программа работает правильно можно только в процессе проверки ее работоспособности, который называется тестирование.

Обычно программа редко сразу начинает работать так, как надо, или

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

Классификация ошибок

Ошибки, которые могут быть в программе, принято делить на три группы:

синтаксические;

ошибки времени выполнения;

логические.

Синтаксические ошибки, их также называют ошибками времени компиляции (Compile-time error), наиболее легко устранимы. Их обнаруживает компилятор. Сообщения о найденных ошибках отображаются в нижней части программы в окне Сообщения / Создание (Messages / Build). Программисту

остается только внести изменения в текст программы и выполнить повторную компиляцию.

Ошибки времени выполнения, в Delphi они называются исключениями

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

Они являются результатом неправильной работы функций, процедур или методов из библиотеки подпрограмм (Run-Time Library – RTL), библиотеки визуальных компонентов (Visual Component Library – VCL) или даже операционной системы (Operating System – OS).

Некоторые из возможных источников перечислены ниже.

1.Компоненты, включенные в программу.

2.Ошибки ввода.

3.Драйверы баз данных.

4.Операционная система.

5.Драйверы устройств, например видеодрайвер.

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

7.Собственные библиотеки RTL или VCL Delphi.

+Следует предвидеть ошибки, которые могут возникать при выполнении программы, и программировать их обработку.

72

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

После возникновения ошибки программист может либо прервать выполнение программы, для этого надо из меню Run выбрать команду Program Reset, либо продолжить ее выполнение, например, по шагам, наблюдая результат выполнения каждой инструкции.

С логическими ошибками дело обстоит иначе. Компиляция программы, в которой есть такая ошибка, завершается успешно и программа ведет себя нормально. Однако при анализе результата работы программы выясняется, что результат неверный. За наиболее часто встречающимися ошибками можно заставить следить саму программу. Для этого в настройках проекта Project Options (Проект → Настройки) на вкладке Compiler (Компилятор) надо выполнить следующие действия:

На панели Code generation (Генерация машинного кода) сбросьте флажок Optimization (Оптимизация). Когда компилятор создает

73

оптимизированный код, он нередко вносит существенные улучшения

вдетали алгоритма, реализуемого в Delphi.

На панели Runtime errors (Ошибки времени выполнения) должны быть установлены флажки Range checking (Контроль выхода индекса за границы массива), I/O Checking (Контроль ошибок ввода/вывода) и Overflow checking (Контроль переполнения при целочисленных операциях).

На панели Debugging (Отладка) установите флажки Debug information (Добавление отладочной информации), Local symbols (Просмотр значений локальных переменных), Reference Info (Просмотр структуры кода), Assertions (Включение процедуры Assert

вмашинный код) и Use Debug DCUs (Использование отладочных версий стандартных модулей библиотеки компонентов VCL). Без указания данных параметров успешная отладка в Delphi будет невозможна.

Для выявления более сложных логических ошибок программист анализирует алгоритм, вручную «прокручивая» его выполнение.

Обработка исключительных ситуаций

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

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

ВDelphi определены две конструкции для обработки локальных исключительных ситуаций: tryexcept и tryfinally, а так же процедура

Assert.

+ Помимо специальных конструкций можно воспользоваться и

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

74

1. Процедура Assert

Данная процедура используется для отладки программ. Её польза заключается в том, что записывается компактно и позволяет добавлять

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

procedure Assert(Condition: Boolean {; Message: String});

У этой процедуры два параметра: первый логическое выражение Condition, второй строка сообщения Message. Если значение выражения равно True, то ничего не происходит, в противном случае возникает искусственно сгенерированная ошибочная ситуация, тогда пользователю выдается сообщение о месте, где расположена данная процедура (имя файла, номер строки) и информационное сообщение (второй параметр). Например:

Assert(I in [2..9], ‘Счетчик вне диапазона’); Assert(N<>0, ‘Делитель не может быть равен нулю’);

Подобные проверки желательно проводить в исходные тексты как можно чаще, чтобы контролировать правильность процесса вычислений. Кроме того в конечной версии приложения, где наличие подобных проверок не требуется, удалять вызовы процедуры Assert из текста не обязательно. Достаточно в коде программы либо указать директиву компилятору {$C-}, либо отключить флажок Assertions (Включение процедуры Assert в машинный код) в

настройках проекта (см. выше).

2. Конструкция try…finally

try

{код программы с возможной ошибкой выполнения} finally

{блок операторов выполняющийся в любом случае} end;

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

Если в любом из операторов, размешенных между try и finally, возникает исключительная ситуация, выполнение передается первому оператору, следующему за командой finally, после чего выполняются все операторы до заключительного end.

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

прежде чем управление будет передано глобальному обработчику исключительных ситуаций или следующему уровню обработки ошибки. Эта

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

75

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

var DynArr : array of integer;

...

try

SetLength (DynArr, 100000);

...

finally

DynArr := nil end;

3.Конструкция try…except

Вотличие от конструкции tryfinally конструкция tryexcept позволяет обрабатывать исключительные ситуации. Вид конструкции представлен ниже:

try

{код программы с возможной ошибкой выполнения} except

{операторы выполняющиеся в случае возникновения исключительной ситуации}

else

{операторы выполняющиеся в случае возникновения не известной исключительной ситуации}

end;

Ключевое слово try (попытка) обозначает начало блока контроля выполнения операторов, следующих до ключевого слова except. В случае

возникновения исключительной ситуации происходит обращение к списку классов, перечисленных перед завершающим словом end. При этом выполняется действие, указанное для соответствующего класса, а затем управление передается первому оператору, следующему за завершающим словом end. Операторы, оставшиеся в части try, пропускаются. Если исключительной ситуации не встретилось, то пропускаются все действия, следующие за ключевым словом except.

Если возникшая ситуация не относится ни к одному обрабатываемому классу, то выполняется команды, указанная после ключевого слова else (необязательная часть).

Назначение конструкции tryexcept. Конструкция tryexcept

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

76

procedure TForml.ButtonlClick(Sender: TObject); var

iCtr: integer; begin

for iCtr := Ord('A') to Ord('Z') do try

ChDir(Char(iCtr)+':\DUKE3D'); MessageDlg('\DUKE3D найдена на диске '+ Char(iCtr), mtlnformation, [mbOK], 0);

Break; except

MessageDlg('\DUKE3D не найдена на диске '+ Char(iCtr), mtError, [mbOK], 0);

end; end;

В данном примере для перебора всех возможных имен дисков от А до Z применяется конструкция fordo. В каждом цикле происходит обращение к процедуре ChDir из RTL с параметром, содержащим имена очередного диска и каталога «DUKE 3D». Если данный каталог не найден на очередном диске, то

при выполнении процедуры ChDir возникает исключительная ситуация типа EInOutError. Выполнение передается первому оператору, следующему за командой except. В данном примере это приводит к выводу сообщения о том, что на текущем диске нет заданного каталога. Если в процедуре ChDir возникла ошибка, следующие за ней операторы вызова процедур MessageDlg и Break не выполняются. Если же каталог найден, то его выполнение приводит

квыходу из цикла и нормальному возврату из метода.

4.Конструкция on…do

Вконструкции tryexcept содержится необязательный элемент, с

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

on <название класса> do <операторы>.

Конструкция ondo допустима только между командой except и заключительной end, как в следующем примере.

try

{Операторы} except

on E: Exception do

{Оператор, возможно, составной} else

{Оператор, возможно, составной} end;

77

Конструкция ondo применяется в случаях, когда дальнейшие действия зависят от типа исключительной ситуации. Кроме того, ее можно использовать

для инициализации неожиданных исключительных ситуаций при обработке ожидаемых. Рассмотрим следующий пример.

try

Assert(y<>5,’’); x := 100 div y;

except

on EZeroDivide do ZeroProc; on EAssertioFailed do

begin

ShowMessage (‘Ошибка #22’); x:=0;

end;

else ShowMessage(‘Непонятно что’); end;

Если в операторе присваивания обнаружена попытка деления на ноль (y=0), то выполнится процедура ZeroProc (определена заранее). Если же значение y окажется равным 5 (функция Assert), то обработчиком исключительных ситуаций будет сгенерирован объект класса EAssertionFailed и выполнится группа операторов в логических скобках. Оператор x:=100 div y при этом будут пропущен. Если встретится какая-то другая исключительная ситуация, то выведется соответствующее сообщение.

Ниже представлена таблица основных классов исключительных ситуаций.

Это исключение

Возбуждается когда ...

EAbort

Необходимо просто прекратить текущий блок кода.

EAccessViolation

Делается попытка получения доступа к недействительному

 

разделу памяти.

EAssertionFailed

Значение выражения в процедуре Assert равно False

 

 

EBitsError

Свойство Bits объекта TBits проиндексировано неправильно.

EControlC

В консоли приложения нажато <Ctrl>+<C>.

EConvertError

StrToInt или StrToFile пытаются выполнить преобразование

 

недействительного значения.

EDivByZero

Попытка деления целого на нуль.

EExternal

Неверное функционирование системы Windows.

EFCreateError

Ошибка во время создания файла.

EInOutError

Обнаружена ошибка любого типа файлового ввода-вывода.

EIntError

Базовый класс, на основе которого созданы классы

 

исключительных ситуаций при работе с целыми числами.

EIntOverflow

Присвоение целого слишком велико для регистра.

EInvalidCast

Попытка неправильного приведения типов посредством as.

EInvalidOp

Произошла одна из множества ошибок с плавающей точкой.

EListError

Ошибка произошла при работе со списком, строкой или

 

объектом списка строк.

78

EMathError

Базовый класс, на основе которого созданы классы

 

исключительных ситуаций при работе с целыми числами.

EMenuError

Произошла ошибка меню.

EOverflow

Присвоение с плавающей точкой слишком велико для регистра.

EOutOfMemory

Нехватка памяти.

EPrinter

Произошла ошибка печати.

EPropReadOnly

Попытка записи в свойство, предназначенное только для чтения.

EPropWriteOnly

Попытка чтения свойства, предназначенного только для записи.

ERangeError

Попытка присвоить целому значение, выходящее за пределы

 

диапазона.

EStringListError

Ссылка на элемент списка вне диапазона списка.

EUnderflow

Присваивание с плавающей точкой слишком мало и

 

устанавливается на 0.

EVariantError

Произошла ошибка с типом variant.

 

 

EZeroDivide

Попытка деления на нуль числа с плавающей точкой.

5.Преднамеренная генерация исключений. Оператор raise

Спомощью ключевого слова raise можно сгенерировать исключение любого типа в любом месте программы. Синтаксис оператора генерации исключения следующий:

raise <исключение>;

Вместо исключения можно указать тип исключения и конструктор raise <тип исключениям>.Create(<текст сообщения>);

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

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

Например, если программа читает текст из окна редактирования Edit1, в котором пользователь должен задать какую-то информацию, то проверить, задана ли информация, можно оператором

...

if Editl.Text=’’ then raise

EZeroDivide.Create('He задана требуемая информация');

...

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

79

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

Отладчик

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

1. Трассировка программы

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

В случае неправильной работы программы необходимо видеть реальный порядок выполнения инструкций. Это можно сделать, выполнив трассировку программы. Трассировка это процесс выполнения программы по шагам (step- by-step), инструкция за инструкцией. Во время трассировки программист дает команду: выполнить очередную инструкцию программы.

Delphi обеспечивает два режима трассировки: без захода в процедуру (Step over) и с заходом в процедуру (Trace into). Режим трассировки без захода в процедуру производит трассировку только главной процедуры, при этом трассировка подпрограмм не выполняется, вся подпрограмма выполняется за один шаг. В режиме трассировки с заходом в процедуру производится трассировка всей программы, то есть по шагам выполняется не только главная программа, но и все подпрограммы.

Для того чтобы начать трассировку, необходимо в меню выбрать Run Step over (Запуск Пошагово) (клавиша F8) или Run Trace into

(Запуск Пошагово с заходом в процедуру) (клавиша F7). В результате в окне редактора кода будет выделена первая инструкция программы. Для выполнения следующей инструкции повторите команду.

В любой момент времени можно завершить трассировку и продолжить выполнение программы в реальном темпе. Для этого надо из меню выбрать

Run Run (клавиша F9).

При необходимости выполнить трассировку части программы следует установить курсор на инструкцию программы, с которой надо начать трассировку, и из меню выбрать Run Run to cursor (Запустить → Запустить от курсора) или нажать клавишу F4. Затем, нажимая клавишу F7 или клавишу F8, выполнить трассировку нужного фрагмента программы.

80

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

2. Точки останова программы

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

Для того чтобы поставить в программу точку останова (breakpoint), необходимо щелкнуть мышью на синей точке, помечающей ту инструкцию программы, перед которой надо поместить точку останова (если в программе нет ошибок, то компилятор помечает выполняемые инструкции программы синими точками). Для точки останова можно задать дополнительные параметры, для этого выберите из меню Run Add Breakpoint (Добавить точку останова), затем из меню следующего уровня команду Source Breakpoint.

В результате открывается диалоговое окно Add Source Breakpoint, в котором

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

которого программа приостановит свою работу в данной точке, вводится в поле

Condition. Количество пропусков данной точки останова можно ввести в поле

Pass count (Число пропусков).

Для того чтобы удалить точку останова, щелкните в окне редактора кода мышью на красной точке, помечающей строку, в которой находится точка останова.

3. Наблюдение значений переменных

Во время отладки, при выполнении программы по шагам,

довольно часто бывает полезно знать, чему равно значение той или иной переменной. Отладчик

позволяет наблюдать значения переменных программы.

Для того чтобы во время выполнения программы по шагам иметь возможность контролировать