Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП_ЛР3,4_A5_Часть2.doc
Скачиваний:
41
Добавлен:
08.11.2019
Размер:
557.06 Кб
Скачать

1.2. Практическая часть

1.2.1. Напишите обработчик события нажатия кнопки 'Изменить': выделенная в списке страницы блокнота строка должна быть заменена содержимым окна редактирования.

1.2.2. Напишите обработчик события нажатия кнопки 'Удалить': выделенная в списке страницы блокнота строка должна быть удалена.

1.2.3. Напишите обработчик события нажатия кнопки 'Очистить': содержимое всего блокнота удаляется.

1.2.4. Модифицируйте программу следующим образом:

a) при выборе записи на странице блокнота соответствующая запись отображается в окне редактирования;

b) строка следующая (предыдущая) за удаленной становится выделенной и отображается в окне редактирования;

c) добавление записи на страницу из окна редактирования происходит при нажатии клавиши <Enter>.

2. Исключительные ситуации

2.1. Теоретическая часть

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

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

Рис. 3

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

Рис. 4

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

1) нажатие <Ctrl-F2> — прекращение выполнения приложения;

2) нажатие <F9> или выполнение команды Run | Run — продолжение работы приложения;

3) нажатие <F7> или <F8> — продолжение отладки по шагам.

При необходимости можно отключить появление сообщений отладчика. Для этого надо выполнить команду Tools | Debugger Options, в открывшемся диалоговом окне выбрать страницу Language Exceptions и на ней выключить опцию Stop On Delphi Exceptions.

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

AssignFile(F,'a.tmp');

Rewrite (F);

New(P);

Erase(F);

Dispose (P);

В программе открывается временный файл с именем a.tmp, чтобы хранить в нем результаты промежуточных вычислений. В конце программы файл должен быть уничтожен процедурой Erase. Кроме того, процедурой New динамически выделяется некоторый объем оперативной памяти. Память будет освобождаться после ее использования процедурой Dispose. Если в промежуточных операторах, помеченных выше точками, возникнет исключение, то вычисления прервутся и процедуры Erase и Dispose не будут выполнены. В результате память, выделенная процедурой New, останется недоступной, а на диске сохранится временный и уже ненужный файл a.tmp.

Избежать подобного рода неприятностей можно несколькими путями:

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

2. Защищать код очистки информационного «мусора» с помощью программного блока try ... finally.

3. Обрабатывать исключения в блоках try ... except.

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

if ( В = 0 ) then ...

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

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

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

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

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

Второй способ борьбы с исключениями состоит в использовании программного блока try ... finally. Блок, содержащий совокупность операторов, способных привести к исключению, можно оформить следующим образом:

try

<операторы, способные привести к исключению и к появлению "мусора">

finally

<операторы, выполняемые в любом случае и производящие зачистку "мусора">

end;

В этом случае операторы в разделе finally будут выполняться всегда, независимо от того, было или не было исключение. После выполнения этих операторов вычисления, как и ранее, прерываются. Если в блоке try были открыты какие-то временные файлы, динамически выделялась память под временные объекты, выполнялись соединения с какими-то базами данных, а после всего этого произошла ошибка, вызвавшая исключение, то в блоке finally можно убрать весь «мусор»: удалить ненужные временные файлы, освободить память от временных объектов, разорвать связь с базой данных.

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

Рассмотренный способ использования блока try ... finally позволяет защитить ресурсы программы внутри блока. Существует также способ глобального выделения и инициализации ресурсов, с защищенной их очисткой. Он состоит в использовании двух специальных включаемых в модуль разделов initialization и finalization. Раздел initialization, выполняемый один раз при первом упоминании в программе данного модуля, можно использовать для однократного выделения необходимых глобальных ресурсов. А раздел finalization, гарантированно выполняемый в конце работы программы независимо от наличия или отсутствия исключений, можно использовать для очистки этих и других ресурсов, затребованных при работе программы. Например, если в процессе работы программы временные файлы с расширениями .tmp могут создаваться в каталоге, имя которого записано в переменной sdirtmp, то гарантированно удалить их при завершении работы программы можно следующими операторами:

var

sSR:TSearchRec; F:File;

ires:integer;

.

initialization

.

finalization

ires := FindFirst(sdirtmp+'*.tmp', faAnyFile, sSR);

while ires=0 do

begin

AssignFile(F, sdirtmp+sSR.name);

Erase(F);

ires := FindNext(sSR)

end;

end.

В этом фрагменте программы с помощью процедуры Erase из каталога, имя которого записано в переменной sdirtmp, удаляются все файлы с расширением .tmp. Поиск этих файлов осуществляется функциями FindFirst и FindNext.

В разделе finalization можно определить, завершается ли программа нормально или в результате генерации исключения, проверив функцию ExceptAddr.

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

Наиболее кардинальным способом борьбы с исключениями является третий из вышеупомянутых - обработка исключений с помощью блоков try ... except. Синтаксис этих блоков следующий:

try

<Исполняемый код>

except

<Код, исполняемый в случае ошибки>;

end;

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

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

on <класс исключения> do <оператор>;

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

try

<Исполняемый код>

except

on E: ESomeException do <обработчик исключения>;

end;

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

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

1. EMathError - класс-предок исключений, случающихся при выполнении операций с плавающей точкой;

EInvalidArgument - значение параметра выходит за диапазон значений

EInvalidOp - передача математическому сопроцессору ошибочной конструкции;

EOverflow - переполнение разрядов при работе со слишком большими величинами;

EUnderflow - потеря разрядов при работе со слишком малыми величинами;

EZeroDivide - деление на ноль.

2. EIntError - класс-предок исключений, случающихся при выполнении целочисленных операций;

EDivByZero - деление на ноль;

EIntOverflow - выполнение операций, приводящих к переполнению целых переменных;

ERangeError - значение целочисленного выражения выходит за пределы установленного целочисленного типа. Попытка обращения к элементу массива по индексу, выходящему за пределы массива.

3. EListError - обращение к элементу списка (String, List, TStringList) по индексу, выходящему за пределы допустимых значений.

4. EInOutError - ошибки при операциях с файловой системой. Код ошибки возвращается в локальной переменной ErrorCode, которая может принимать следующие значения:

2 - файл не найден;

3 - неверное имя файла;

4 - слишком много открыто файлов;

5 - отказ в доступе;

100 - конец файла;

101 - диск полон;

106 - неверная операция ввода.

5. EConvertError - ошибки преобразования типов (простых, объектных).

Операторы on...do позволяют проводить выборочную обработку различных исключений. Пример такой обработки:

var A : shortint;

try

С := StrToInt(Editl.text); A := В div С;

except

on EConvertError do

MessageDlg('Вы ввели ошибочное число; повторите ввод', mtWarning, [mbOk], 0);

on EDivByZero do

MessageDlg('Вы ввели нуль; повторите ввод', mtWarning, [mbOk], 0) ;

on EIntOverflow do

if (B*C) >= 0 then A := 32767 else A := -32767;

end;

В этом примере выполняются чтение целого числа, введенного пользователем в окно редактирования Editl, и деление на него переменной В. Если пользователь ввел не целое число (например, по ошибке нажал не цифру, а какой-то буквенный символ), то при выполнении функции StrToInt возникнет исключение класса EConvertError. Соответствующий обработчик исключения сообщает пользователю о сделанной ошибке и советует повторить ввод. Аналогичная реакция следует на ввод пользователем нуля (класс исключения EDivByZero). Если же при делении возникает целочисленное переполнение (класс исключения Elnt-Overflow), то результату присваивается максимальное положительное или отрицательное значение.

Некоторые исключения имеют дополнительные поля (свойства), уточняющие вид ошибки. Например, это относится к исключению файлового ввода/вывода EInOutError, которое имеет свойство errorcode типа integer. Это свойство содержит стандартный для Object Pascal номер ошибки файлового ввода/вывода.

Чтобы воспользоваться полями исключений, оператор on надо записывать в виде

on <имя>: <класс исключения> do

<операторы с конструкциями <имя>.<имя поля>>;

Содержащееся в этом операторе <имя> носит сугубо локальный характер, нигде ранее определяться не должно и вводится только для того, чтобы можно было сослаться на поле по имени объекта исключения.

Пример использования полей при операциях файлового ввода/вывода (подразумевается, что в переменной filename типа string хранится имя обрабатываемого файла):

on IO: EInOutError do

begin

Case IO.errorcode of

2: s:='Файл ''' +s+''' не найден';

3: s:='Ошибочное имя файла ''' + s+ ' ' ' ' ;

4: s:='Слишком много открытых файлов';

5: s:='Файл ''' +s+''' не доступен';

100: s:= 'Достигнут конец файла ''' +s+'’'’;

101: s:= 'Диск переполнен при работе с файлом ''' + s + ' ' ' ' ;

106: s:= 'Ошибка ввода при работе с файлом ' ' ' +s+ ' ' ' ' ;

end;

MessageDlg(s, mtWarning, [mbOk], 0);

end;

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

Помимо операторов on, обрабатывающих заданные типы исключений, в раздел except может быть включен оператор else, в котором выполняется обработка всех не перехваченных ранее исключений, т.е. происходит обработка по умолчанию. Например:

except

on . . . ;

on . . . ;

else <обработчик всех не перехваченных ранее событий>;

end;

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

В раздел except могут включаться или только операторы on, или только какие-то другие операторы. Смешение операторов on с другими не допускается.

Раздел except может включаться совместно с описанным ранее разделом finally. Например, можно организовать блоки try следующим образом:

try

try

finally

end;

except

end;

Блоки try...except могут быть вложенными явным или неявным образом. Примером неявной вложенности является блок try...except, в котором среди операторов раздела try имеются вызовы функций или процедур, которые имеют свои собственные блоки try...except. Рассмотрим последовательность обработки исключений в этих случаях. При генерации исключения сначала ищется соответствующий ему обработчик on в том блоке try...except, в котором создалась исключительная ситуация. Если соответствующий обработчик не найден, поиск ведется в обрамляющем блоке try...except (при наличии явным образом вложенных блоков) и т.д. Если в данной функции или процедуре обработчик не найден или вообще в ней отсутствуют блоки try...except, то поиск переходит на следующий уровень — в блок, из которого была вызвана данная функция или процедура. Этот поиск продолжается по всем уровням. И только если он закончился безрезультатно, выполняется стандартная обработка исключения, заключающаяся, как уже было сказано, в выдаче пользователю сообщения о типе исключения.

Как только оператор on, соответствующий данному исключению, найден и выполнен, объект исключения разрушается и управление передается оператору, следующему за соответствующим блоком try...except.

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

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

begin

assignfile(f, 'data.dan');

try

append(f);

try

writeln (f,s);

finally

closefile(f) ;

end;

except

on E: EInOutError do

if E.ErrorCode = 2 then

if MessageDlg('Файл не найден. Создать его?', mtError, [mbYes, mbNo],0)=mrYes

then FileCreate('data.dan');

end;

end;

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

Внешний блок tryexcept используется для обработки исключительной ситуации, которая может произойти в программе. После закрытия файла во внутреннем блоке finally блок except использует временный объект Е для определения типа произошедшей ошибки ввода/вывода.

Пример. Модификация программы «Блокнот», созданной в части 1.

2.1.1. Разработаем метод, который при закрытии программы все страницы блокнота будет сохранять в отдельных файлах в каталоге, указанном в переменной dir.

В разделе public описания класса TNoteBookF опишите метод SaveAll и переменную dir:

public

dir: string;

procedure SaveAll;

В разделе Implementation модуля опишите программный код метода:

procedure TNoteBookF.SaveAll;

var i: integer;

TempComponent: TListBox;

begin

InputQuery('Введите полное имя каталога ' , ‘’,dir);

for i:=0 to 11 do

begin

NoteBookl.PageIndex:=i;

TempComponent:=

TListBox(FindComponent('ListBox'+IntToStr(i + 1))) ;

TempComponent.Items.SaveToFile(dir+’Месяц'+intToStr(i+1));

{Строки компонента ListBox сохраняются в соответствующем файле}

end;

end;

Описанный метод должен использоваться как дополнительный обработчик кнопки ‘Закрыть’ и выполняться до основного обработчика Close.

2.1.2. Сохраните и запустите приложение. Убедитесь в работоспособности приложения для существующих каталогов. Попробуйте сохранить файлы в несуществующий каталог.

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

2.1.3. Чтобы обезопасить приложение от подобного вида ошибок, добавим в обработчик блок обработки исключительной ситуации EFCreateError, возникающей при сохранении файла:

begin

InputQuery('Введите полное имя каталога ', ‘’ , dir);

if dir[length(dir)]<>'\' then dir:=dir+'\';

{в конец строки добавляется разделитель подкаталогов}

for i:=0 to 11 do

begin

NoteBookl.PageIndex:=i;

TempComponent:=

TListBox(FindComponent('ListBox'+IntToStr(i+1)));

try

TempComponent.Items.SaveToFile(dir+'Месяц'+intToStr(i+1));

{Строки компонента ListBox сохраняются в соответствующем файле}

except

on EFCreateError do

begin

{класс исключений, возникающих при неудачных попытках создания файла}

{далее размещается текст обработчика}

MessageBox(0,'Не верно задан путь к каталогу!','ОШИБКА',MB_ICONERROR);

if IDOK=1 then

begin

InputQuery('Введите правильное имя каталога','На диске D:',dir);

if dir[length(dir)]<>'\' then dir:=dir+'\';

TempComponent.Items.SaveToFile(dir+'Месяц'+intToStr(i+1));

end;

end;

end;

end;

end;

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]