Е. К. Пугачев Объектно-ориентированное программирование Под общей редакцией Ивановой Г. С. Рекомендовано Министерством общего и профессионального образования Российской Федерации в качестве учебник

Вид материалаУчебник

Содержание


5.8.Обработка исключений
Структура фрагментов с исключениями
Создание исключений
Type Exception = class(TObject)
Constructor CreateRes(Ident: Integer; Dummy: Extended = 0)
Constructor CreateHelp(const Msg: string; AHelpContext: Integer)
Constructor CreateResHelp(Ident: Integer; AHelpContext: Integer)
Генерация исключений
Обработка исключений
Unit MasByte
Procedure SetEl(Ind:byte;m:byte)
Function TMasByte.GetEl(Ind:byte):byte
Подобный материал:
1   ...   31   32   33   34   35   36   37   38   39
^

5.8.Обработка исключений


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

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

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

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

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

Традиционный подход

Использование исключений

if n<>0 then x:=A/n

else <действия по устранению ошибки >

try x:=A/n;

except

on EDivByZero do

<действия по устранению ошибки >

end;

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

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

К средствам обработки исключений относятся:

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

- иерархия классов различных исключений, определенная в Delphi;

- оператор генерации исключения;

- операторы обработки исключений.

^ Структура фрагментов с исключениями. Фрагменты программ, использующих исключения, оформляются с использованием двух специальных конструкций try …finally и try … except, которые определяются следующим образом:

try <операторы, при выполнении которых могут возникнуть исключения>

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

end;

try <операторы, при выполнении которых могут возникнуть исключения>

except <операторы, которые выполняются только при возникновении исключения>

end;

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

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

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

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

^ Создание исключений. Базовым классом для всех исключений является класс Exception. Этот класс предусматривает достаточно большое количество конструкторов, которые формируют объекты различной структуры. Основные свойства класса Exception - свойства Сообщение и Контекст помощи. В Delphi 4 класс этот класс описывается следующим образом:

^ Type Exception = class(TObject)

private

FMessage: string;

FHelpContext: Integer;

public

Constructor Create(const Msg: string);{объект содержит сообщение, заданное строкой}

Constructor CreateFmt(const Msg: string;

const Args: array of const); { объект содержит сообщение, заданное строкой, дополненной форматируемыми параметрами}

^ Constructor CreateRes(Ident: Integer; Dummy: Extended = 0); {объект содержит сообщение, получаемое из файла ресурса по его идентификатору}

Constructor CreateResFmt(Ident: Integer;

const Args: array of const); {объект содержит сообщение, получаемое из файла ресурса по его идентификатору, причем строка сообщения может дополняться форматируемыми параметрами}

^ Constructor CreateHelp(const Msg: string; AHelpContext: Integer); {объект содержит сообщение и контекст помощи}

Constructor CreateFmtHelp(const Msg: string;

const Args: array of const; AHelpContext: Integer); {объект содержит сообщение с форматируемыми параметрами и контекст помощи }

^ Constructor CreateResHelp(Ident: Integer; AHelpContext: Integer); {объект содержит сообщение, получаемое из файла ресурса, и контекст помощи}

Constructor CreateResFmtHelp(Ident: Integer;

const Args: array of const; AHelpContext: Integer); { объект содержит сообщение с форматируемыми параметрами, получаемое из файла ресурса, и контекст помощи }

property HelpContext: Integer

read FHelpContext write FHelpContext; {контекст помощи}

property Message: string read FMessage write FMessage; {строка сообщения}

end;

Основное назначение класса исключения - идентификация групп ошибок: отнесение исключения к тому или другому классу определяет способ обработки данного исключения (см. далее).

Потомками класса Exception являются, например, следующие классы исключений:

EDivByZero = class(EIntError); {деление на ноль в целочисленной арифметике}

ERangeError = class(EIntError); {обращение элементам массива по несуществующим индексам}

EIntOverflow = class(EIntError); {переполнение в целочисленной арифметике}

EMathError = class(EExternal); {ошибки арифметики с плавающей точкой}

EInvalidOp = class(EMathError); {неверный операнд)

EZeroDivide = class(EMathError); {деление на ноль в арифметике с плавающей точкой}

EOverflow = class(EMathError); {переполнение в арифметике с плавающей точкой}

EUnderflow = class(EMathError); {исчезновение порядка в арифметике с плавающей точкой}

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

^ Генерация исключений. Исключения могут генерироваться автоматически (при обнаружении той или иной аварийной ситуации операционной системой) и программно (по мере надобности). Для программной генерации исключений используется специальный оператор raise, за которым обычно следует вызов одного из конструкторов класса-исключения, например:

if n=0 then raise EDivByZero.Create('Количество отрезков равно 0.');

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

^ Обработка исключений. Обработка исключений выполняется в секции except конструкции try … except с помощью специальной конструкции типа case:

except

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

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

. . .

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

else <оператор>

end;

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

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

1 способ. Можно использовать формальную переменную, которая связывается с объектом-исключением уже в процессе обработки. Эта переменная описывается только внутри варианта on и доступна также только внутри одной альтернативы:

try . . .

except

on E:EDivByZero do {объявление переменной с указанием типа}

begin

<переменная E доступна и может использоваться для обращения к полям и методам объекта, например, E.Message>

end;

else <переменная E – не доступна>

end;

2 способ. Можно использовать функцию

function ExceptionObject:TObject;

Эта функция возвращает объектную ссылку на текущее исключение, при отсутствии исключений она возвращает nil:

try <контролируемые действия>

except

on EDivByZero do

ShowMessage(EDivByZero(ExceptionObject).Message); {используется явное преобразование типов}

end;

Если используется вложение конструкций try … except, то можно перепоручить обработку исключения секции except внешней конструкции, указав в секции except внутренней конструкции оператор raise:

try . . .

try . . .

except

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

. . .

else raise;

end

except

<обработка остальных исключений>

end;

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

var ErrorAddr: Pointer

При генерации исключения для помещения адреса ошибки в это поле используется конструкция raise … at …, например:

raise Exception.Create('Исключение с адресом') at ErrorAddr;

Пример 5.81. Использование исключений (класс «Динамический массив» - вариант 2)

Проиллюстрируем использование исключений на примере разработки класса «Динамический массив», рассмотренном в примере 5.4.

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

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

1) попытка записать элемент за пределами выделенной памяти;

2) попытка обращения (чтения) или записи элемента за пределами используемой части массива, за исключением добавления элемента за последним;

3) попытка преобразовать в число пустую строку или строку, содержащую недопустимые элементы.

Эти ситуации будут обнаружены в трех методах: в методе SetEl, в методе GetEl и в методе InputMas.

Соответственно, описываем в модуле новые исключения и генерируем их вышеуказанных методах:

^ Unit MasByte;

Interface

Uuses SysUtils,Dialogs, Grids;

Type

TMas=array[1..255] of byte;

TMasByte = class(TObject)

private

ptr_an: ^TMas; {указатель на массив}

len:byte; {максимальная длина массива}

Name:string;{для выдачи диагностики будем хранить имя объекта}

^ Procedure SetEl(Ind:byte;m:byte);

Function GetEl(Ind:byte):byte;

public

n:Byte; {реальный размер массива}

Constructor Create(an:byte;aName:string);

Destructor Destroy;override;

Property Mas[Ind: byte]:byte read GetEl write SetEl;default;

Procedure Modify(Ind:byte;Value:byte);

Procedure Insert(Ind:byte;Value:byte);

Function Delete(Ind:byte):byte;

Function InputMas(Grid:TStringGrid;I,J:integer):boolean;

Procedure OutputMas(Grid:TStringGrid;I,J:integer);

end;

EInputError=class(Exception);{дополнительное исключение}

Implementation

Constructor TMasByte.Create;

Begin

inherited Create;

GetMem(ptr_an,an);

len:=an; Name:=aName;

End;

Destructor TMasByte.Destroy;

Begin

FreeMem(ptr_an);

inherited Destroy;

End;

Procedure TMasByte.SetEl(Ind:byte;m:byte);

Begin

if Ind<=len then

if Ind<=n then ptr_an^[Ind]:=m

else

raise ERangeError.CreateFmt('В массиве %s нет %d-го элемента.',[Name,Ind]) {генерация исключения}

else

raise ERangeError.CreateFmt('В массиве %s можно разместить только %d элементов.',[Name,Len]); {генерация исключения}

End;

^ Function TMasByte.GetEl(Ind:byte):byte;

Begin

if Ind<=n then Result:=ptr_an^[Ind]

else

raise ERangeError.CreateFmt('В массиве %s нет %d-го элемента.', [Name,Ind]); {генерация исключения}

End;

Function TMasByte.InputMas(Grid:TStringGrid;I,J:integer):boolean;

Var k:byte; x,er_code:integer;

Begin

with Grid do

begin

k:=0;

Result:=true;

while (Cells[k+I,J]<>'')and Result do

begin

Val(Cells[k+I,J],x,er_code);

if er_code=0 then

if x<=255 then Insert(k+1,x)

else

begin

raise EInputError.Create('Значение не может превышать 255.'); {генерация исключения}

Result:=false;

exit;

end

else

begin

raise EInputError.Create('В строке обнаружены недопустимые символы.'); {генерация исключения}

Result:=false;

exit;

end;

k:=k+1;

end;

OutputMas(Grid,I,J);

end;

End;

. . . {тексты остальных методов не изменились}

end.

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

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

A:=TMasByte.Create(10,'A'); {конструируем объект A}

try A.InputMas(StringGrid,0,0);{пробуем ввести массив}

except

on E:EInputError do {если обнаружено исключение ввода}

MessageDlg(E.Message,mtInformation,[mbOk],0);

end;

Аналогично, при попытке записать что-либо в массив следует предусматривать возможность возникновения аварийной ситуации:

try A[Ind]:=Value; {пробуем занести значение в массив}

except

on E:ERangeError do {если обнаружено исключение «неверный индекс»)

MessageDlg(E.Message,mtInformation, [mbOk], 0);

end;

Вопросы к главе 5
  1. Дайте определение класса, принятое в Delphi Pascal. Какие новые возможности определения классов появились в этом языке программирования по сравнению с Pascal 7.0? Сравните их с аналогичными средствами, существующими в С++3.1?
  2. Какие виды полиморфизма реализованы в данной среде? Дайте определение абстрактным и динамическим методам? Поясните, чем они отличаются от обычных виртуальных методов. Определите сущность перегрузки методов.
  3. Определите понятие «свойство». С какой целью целесообразно использовать механизм свойств? Приведите примеры.
  4. Что такое «информация о типе времени выполнения»? Зачем она используется? Дайте определение метакласса и поясните, для чего он может быть использован. Какую роль играют методы класса и почему их можно вызывать без указания имени объекта?
  5. Поясните сущность понятия «делегирование методов». Какие средства должен включать язык, в котором возможна реализация делегирования?
  6. Как построена библиотека VCL? Чем различаются отношения «основной/вспомогательный» и «старший/младший»? Как их можно использовать?
  7. Какие средства создания сообщений предлагаются средой Delphi? В каких случаях возникает необходимость создания новых сообщений? Как описывается обработчик сообщений? Как генерировать новые события?
  8. Какие ситуации попадают под понятие «исключительные»? Почему возникла необходимость создания средств обработки исключений? Поясните процесс создания/обработки исключений. Перечислите средства, позволяющие реализовать данный процесс.