Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

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




Структурная обработка исключительных ситуаций (ИС) – это система, позволяющая программисту при возникновении ошибки выполнить код программы, подготовленный для обработки такой ошибки. Это выполняется с помощью языковых конструкций, которые как бы охраняют фрагмент кода программы и определяют обработчики ошибок, которые будут вызываться, если что-то пойдет не так в охраняемом участке кода. В данном случае понятие ИС относится к языку и его не нужно путать с системными ИС (hardware exceptions), такими как General Protection Fault. Эти ИС обычно используют прерывания и особые состояния аппаратуры для обработки критичной системной ошибки. ИС в C++ независимы от “железа”, не используют прерываний и используются для обработки таких ошибочных состояний, которые в программе не предусмотрены. Системные ИС, конечно, могут быть перехвачены и преобразованы в языковые ИС, но это только одно из применений языковых ИС.

Механизм обработки ИС, впервые реализованный в Windows NT и затем внедренный во все операционные системы Microsoft и языковые процессоры, позволяет программисту избавиться от рутинных операций по многочисленным проверкам и реакции на их результаты.

При традиционной обработке ошибок, обнаруженные в подпрограмме ошибки обычно передаются наружу (во "внешнюю" подпрограмму) в виде возвращаемого значения функции, параметров или глобальных переменных (флажков). Каждая вызывающая подпрограмма должна проверять результат вызова на наличие ошибки и выполнять соответствующие действия. Часто, это просто выход еще выше, в подпрограмму более высокого уровня и т.д.: функция A вызывает B, B вызывает C, C обнаруживает ошибку и возвращает код ошибки в B, B проверяет возвращаемый код, видит, что возникла ошибка и возвращает код ошибки в A, A проверяет возвращаемый код и выдает сообщение об ошибке либо решает сделать что-нибудь еще, раз первая попытка не удалась.

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

Структурная обработка ИС заменяет ручную обработку ошибок автоматической, сгенерированной компилятором. В приведенном выше примере, процедура A установила бы “охрану” на фрагмент кода, в котором вызывается B. B просто вызывает C. Когда C обнаруживает ошибку, то создает (throw – ключевое слово языка С++) ИС. (Помимо глагола «создает» часто используют термины «выбрасывает» и «генерирует».) Специальный код, сгенерированный компилятором, начинает поиск обработчика данной исключительной ситуации. При поиске “защищенного” участка кода используется информация, сохраненная в стеке. В процедурах C и B нет такого участка, а в A - есть. Если один из обработчиков ошибок, которые используются в A, подходит по типу для возникшей в функции C исключительной ситуации, то программа переходит на его выполнение. При этом область стека, используемая в B и C, очищается и выполнение этих функций прекращается.

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

 

Замечание. На самом деле это не совсем так. Если в программе не найден обработчик возникшей ИС или если в функции, объявленной как не генерирующей ИС, ИС все-таки генерируется, то вызывается глобальная функция unexpected(). По умолчанию, эта функция просто прекращает выполнение программы путем вызова функции terminate(), abort() или exit(int). Вместе с тем с помощью функции set_unexpected(unexpected_handler ph) программист может установить свою функцию-обработчик необработанных ИС, которая должна иметь сигнатуру unexpected_handler. Кстати, использование функций terminate(), abort() или exit(int) для завершения работы программы не является лучшим выходом (это можно сравнить с хлопаньем дверью); «культурно» завершить работу программы можно путем вызова функции PostQuitMessage(int).

 

Без проверки возвращаемого кода после каждого вызова подпрограммы код программы упрощается, а скомпилированный код – выполняется быстрее. При наличии ИС подпрограмма B не должна содержать дополнительный код для проверки возвращаемого результата и передачи его в A. Функция B ничего не должна делать для передачи ИС, возникшей в C, в процедуру A - встроенная система обработки ИС делает всю работу.

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

 

Модель ИСв С++ и Object Pascal является невозобновляемой (non-resumable). Это значит, что при возникновении ИС невозможно вернуться в точку, где она возникла, для продолжения выполнения программы (это позволяет сделать возобновляемая – resumable – модель). Невозобновляемые ИС разрушают стек, сканируя его в поисках обработчика. В возобновляемой модели необходимо сохранять стек, состояние регистров процессора в точке возникновения ошибки и выполнять поиск обработчика и его выполнение в отдельном стеке. Возобновляемую систему обработки исключительных ситуаций гораздо труднее создать и применять, нежели невозобновляемую.

Литература. См. [2-5] и MSDN.

 


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

Для обработки ИС в С++ используются такие основные понятия, как блоки try и catch и оператор throw, а также предопределенный класс exception и производные от него. 

 

Блоки обработки ИС.

Чтобы перехватить ИС, поставьте перед блоком ключевое слово try и поместите после него одну или несколько секций catch, которые называются обработчиками ИС (exception handlers):

 

Try

{

// Фрагмент кода, который может инициировать исключения (охраняемый)

}

Catch (Exception_Type_1 t)

{

//Обработка ИС типа Exception_Type_1

}

Catch (Exception_Type_2 t)

{

//Обработка ИС типа Exception_Type_2

}

//Другие блоки catch

Catch (...)

{

//Обработка ИС всех остальных типов

}

//продолжение выполнения программы после обработки ИС

 

Каждый обработчик, за исключением обработчика с многоточием, соответствует одному конкретному типу ИС. Если во фрагменте, называемом try-блоком, инициируется исключение, компилятор просматривает список обработчиков в порядке их перечисления и ищет обработчик, подходящий по типу сгенерированной ИС. Многоточие соответствует исключениям любого типа; если такой обработчик присутствует (а он не является обязательным), он должен находиться последним в списке. Обработчик с многоточием может быть и единственным, в этом случае он в состоянии обработать ИС любого типа:

Try

{

// Фрагмент кода, который может инициировать исключения (охраняемый)

}

Catch (...)

{

//Обработка ИС всех типов

}

//продолжение выполнения программы после обработки ИС

 

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

 

Выполнение программы после возникновения ИС.

Если выполнение try-блока обходится без возникновения ИС, программа благополучно игнорирует все обработчики и продолжает работу с первого выражения за последним обработчиком. Если же исключение все же произошло, оно будет единственным из всего списка, и после его обработки выполнение программы продолжится за последним обработчиком списка.

Существуют два исключения из этих правил.

Первое. Обработчик может содержать вызов крамольного goto или инициировать еще одну ИС. Если обработчик инициирует ИС, то это может быть повторный вызов ИС того же типа или другого:

 

Catch(int exception)

{

//Что-то делаем, а затем …

throw(“Помогите, спасите!”); //Инициируется исключение типа char*

}

 

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

Второе. Если исключение не перехвачено, т.е. для ИС не нашлось ни одного обработчика, по умолчанию вызывается глобальная функция unexpected(). Она, в свою очередь, просто прекращает выполнение программы путем вызова функции terminate(), abort() или exit(int). Вместе с тем с помощью функции set_unexpected(unexpected_handler ph) программист может установить свою функцию-обработчик необработанных ИС, которая должна иметь сигнатуру unexpected_handler. Заметьте, что Ваша функция завершения просто обязана завершить программу и не может инициировать другие ИС. Она может выполнить необходимые подготовительные действия, но никогда не должна возвращать управление вызывающей программе, так как из этого ничего путного не выйдет.

 

Вложенные блоки обработки ИС.

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

Try

{

// Ненадежный фрагмент fr_1

try

{

  // Еще один ненадежный фрагмент fr_2

  try

  {

    // И еще один… fr_3

  }

  catch(...)

  {

//обрабочик ИС, возникшей во фрагменте fr_3

  }

}

catch(...)

{

// обрабочик ИС, возникшей во фрагменте fr_2

}

}

Catch(...)

{

// обрабочик ИС, возникшей во фрагменте fr_1

}

 

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

 

Внешние ИС не перехватываются!

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

 










Последнее изменение этой страницы: 2018-04-12; просмотров: 485.

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