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

книги / Объектно-ориентированное программирование

..pdf
Скачиваний:
11
Добавлен:
12.11.2023
Размер:
16.61 Mб
Скачать

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

ProgressBar.StepIt; {вывести в окно изображения ProgressBar очередной закрашенный прямоугольник}

if Cancel then break; {если установлено свойство Cancel, то прекратить обработку}

end;

ProgressBar.Free; {уничтожить объект TProgressBar} Button.Free; {уничтожить кнопку TButton}

Canvas. TextOut(70,20, 'Вывод завершен); end;

Procedure TForml.ButtonClick; {обработчик нажатия на кнопку Прервать} Begin Cancel:=true; End; {устанавливаем свойство Cancel}

Procedure TForml.ExitButtonClick(Sender: TObject); Begin Close; End;

End.

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

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

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

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

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

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

251

5. Объектная модель Delphi Pascal

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

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

if n o O then x:=A/n

try x:=A/n;

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

except

ошибки >

on EDivByZero do

 

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

 

end;

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

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

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

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

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

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

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

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

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

возникновении исключения>

end;

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

исключения>

end;

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

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

252

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

закрыть открытые файлы или удалить с экрана форму.

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

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

Создание исключений. Базовым классом для всех исключений является класс 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 o f const); { объект содержит сообщение, заданное строкой, дополненной форматируемыми параметрами}

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

Constructor CreateResFmt(Ident: Integer;

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

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

Constructor CreateFmtHelp(const Msg: string;

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

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

Constructor CreateResFmtHelpfJdent: Integer;

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

property HelpContext: Integer

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

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

253

5. Объектная модель Delphi Pascal

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

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

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

несуществующим индексам}

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

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

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

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

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

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

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

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

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

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

except

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

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

254

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

else <оператор> end;

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

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

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

try . . .

except

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

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

end;

else <переменная Е - не доступна> end;

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

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

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

on EDivByZero do

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

end;

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

try • • •

try . . .

except

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

255

5. Объектная модель Delphi Pascal

else raise; end

except

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

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

var ErrorAddr: Pointer

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

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

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

5.4.

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

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

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

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

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

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

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

Unit MasByte;

Interface

Uuses SysUtils,Dialogs, Grids;

Type

256

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

TMas~array[l..255] o f byte; TMasByte = class(TObject)

private

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

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

ifInd< =len then

iflnd<=n thenptr_anA[Ind\:=m else

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

else

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

End;

257

5. Объектная модель Delphi Pascal

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

ifInd< =n then Result: =ptr_anA[Ind\ else

raise ERangeError.CreateFmtf массиве %s нет %й-го элемента.', [Name,Ind\y, {генерация исключения}

End;

Function TMasByte.InputMas(Grid:TStringGrid;I,J:integer):boolean; Var kbyte; x,er_code:integer;

Begin

■with Grid do begin

k: -0; Result: =true;

while (Cells[k+I,J\<> ”)andResult do begin

Val(Cells[k+I,J\,x,er_code); if er_code=0 then

ifx< =255 then Insert(k+l,x) else

begin

raise EInputError.Create( 'Значение не может превышать 255.');

(генерация исключения}

Result: =false; exit;

end else begin

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

Result: =false; exit;

end;

k:=k+l;

end;

OutputMas(Grid,I,J);

end;

End;

. . .

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

end.

 

258

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

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

A:=TMasByte.Create(10, 'А); {конструируем объект А} 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;

Вопросы для самоконтроля

1.Дайте определение класса, принятое в Delphi Pascal. Какие новые возможности определения классов появились в этом языке программирования по сравнению с Pascal 7.0? Сравните их с аналогичными средствами, существующими в С++3.1?

2.Какие виды полиморфизма реализованы в данной среде? Дайте определение абстрактным и динамическим методам? Поясните, чем они отличаются от обычных виртуальных методов. Определите сущность перегрузки методов.

3.Определите понятие «свойство». С какой целью целесообразно использовать механизм свойств? Приведите примеры.

4.Что такое «информация о типе времени выполнения»? Зачем она используется? Дайте определение метакласса и поясните, для чего он может быть использован. Какую роль играют методы класса и почему их можно вызывать без указания имени объекта?

5.Поясните сущность понятия «делегирование методов». Какие средствадолжен включать язык, в котором возможна реализация делегирования?

6.Как построена библиотека VCL? Чем различаются отношения «основной/ вспомогательный» и «старший/младший»? Как их можно использовать?

7.Какие средства создания сообщений предлагаются средой Delphi? В каких случаях возникает необходимость создания новых сообщений? Как описывается обработчик сообщений? Как генерировать новые события?

8.Какие ситуации попадают под понятие «исключительные»? Почему возникла необходимость создания средств обработки исключений? Поясните процесс создания/обработки

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

6. ОБЪЕКТНАЯ МОДЕЛЬ C++ BUILDER

Объектная модель C++ Builder несколько отличается от первоначальной объектной модели C++, описанной в главе 3. Прежде всего, она базируется на современной усложненной модели, используемой в последних версиях языка C++, т.е. поддерживает пространства имен, исключения и специальные средства преобразования типов.

Кроме этого, C++ Builder использует библиотеку классов VCL, разработанную для среды Delphi и основывающуюся на объектной модели Delphi Pascal. Следовательно, при создании C++ Builder необходимо было согласовать конструкции C++ и Pascal Delphi, а также механизмы реализации этих конструкций, обращая особое внимание на те возможности, которые есть в Delphi Pascal, но отсутствуют в C++. В результате в объектную модель C++ были добавлены:

-возможность создания специальных секций для описания опубликованных элементов класса и элементов, реализующих OLE-механизм;

-средства объявления свойств;

-определения специальных классов, моделирующих стандартные типы данных Delphi Pascal (множества, строки и т.д.);

-возможность определения указателей на методы (_ ^closure);

-специальный модификатор (__ declspec), посредством которого реализуются, например, динамические методы Delphi Pascal.

Различие объектных моделей и их реализаций в C++ и Delphi Pascal не позволяет обеспечить полную совместимость классов, разработанных в этих языках. Поэтому разработчики C++Builder обеспечили возможность создания двухтипов классов: обычные классы C++ с расширенными возможностями и VCL-совместимые классы - для работы с библиотекой визуальных компонент VCL.

6.1. Расширение базовой объектной модели C++

Как уже говорилось выше, объектная модель C++ Builder включает ряд новых (по сравнению с Borland C++ 3.1) средств. Это средства:

-определения пространств имен;

-описания указателей на методы;

-определения и переопределения типа объекта;

-описания свойств.

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

260