4887

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

Лекция

Информатика, кибернетика и программирование

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

Русский

2012-11-28

43.5 KB

3 чел.

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

Исключением называют возникновение «аномальной» ситуации во время выполнения, которое программа может обнаружить, например: деление на 0, выход за границы массива или отсутствие требуемого количества свободной памяти. Такие ситуации нарушают нормальный ход работы программы и требуют немедленной реакции. В C++ имеются встроенные средства для оповещения об исключительной ситуации и её обработки. С помощью этих средств активизируется механизм, позволяющий двум несвязанным (или независимо разработанным) фрагментам программы обмениваться информацией об исключении.

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

// Коды статуса, возвращаемые функциями

enum Status

{

  SUCCESS = 0,         // Признак успешного выполнения

  NEGATIVE_ROOT_ERROR, // Ошибка при попытке вычислить квадратный

                       // корень отрицательного аргумента

  ZERO_DIVISION_ERROR  // Ошибка при попытке деления на 0

};

// Функция вычиляет квадратный корень sqrt(val)

Status squareRoot( double val, double * result )

{

  if ( val < 0.0 )

     return NEGATIVE_ROOT_ERROR;

  * result =  std::sqrt( val );

  return SUCCESS;

}

// Функция выполняет деление a / b

Status divide( double a, double b, double * result )

{

  if ( b == 0 )

     return  NEGATIVE_ROOT_ERROR;

  * result = a / b;

  return SUCCESS;

}

// Функция вычисляет sqrt( a / b )

Status sqrtDiv( double a, double b, double * result )

{

  double d;

  Status s = divide( a, b, & d );

  if ( s != SUCCESS )

     return s;

  s = squareRoot( d, result );

  if ( s != SUCCESS )

     return s;

  return SUCCESS;

}

void main()

{

  Double result;

  Status s = sqrtDiv( 5, -1, & result );

  

  if ( s != SUCCESS )

  {

     std::cout << "Ошибка с кодом " << s << std::endl;

     return;

  }

}

С использованием механизма исключительных ситуаций проверка ошибок организуется гораздо проще и элегантнее. Когда встречается аномальная ситуация, та часть программы, которая её обнаружила, может возбудить исключение с помощью инструкции throw (англ. бросить) – сам объект-исключение представляет собой обычную переменную любого типа (как правило, встроенные типы для этого не используются, гораздо удобнее описать собственный тип с включением в него дополнительной информации об аномальной ситуации).  С этого места выполнение аномального фрагмента программы останавливается и начинается поиск подходящего блока-обработчика, соответствующего типу возбужденного исключения.

Блок-обработчик описывается инструкцией catch(arg) (англ. поймать) с аргументом, описывающим тип перехватываемого этим блоком исключения (напоминает определение функции с одним параметром). Блоков-обработчиков может быть несколько, все они описываются последовательно сразу за блоком-инициатором проверки исключений, описываемым ключевым словом try (англ. пробовать). При возбуждении где-либо в пределах try-блока исключения, последующий список catch-блоков будет просматриваться последовательно до тех пор, пока какой-либо из них не окажется способным перехватить возбужденное исключение, в этом случае, управление передается в этот catch-блок, а аргумент catch-блока принимает значение возбужденного исключения (аналогично вызову функции с одним параметром). Если ни один из catch-блоков не способен перехватить исключение, то процесс поиска продолжится во внешнем (вызывающем) коде. Если же подходящий обработчик исключения не обнаружится вовсе, то программа будет аварийно завершена. Для перехвата всех исключений, вне зависимости от их типа, исользуется специальный универсальный блок-обработчик catch(...), который должен быть последним в списке всех блоков-обработчиков. Разумеется, в таком обработчике сам объект-исключение недоступен, поскольку неизвестен его тип. Следующий пример иллюстрирует простейшее использование обработки исключений:

  try // Попытка выполнить "опасный" участок кода

  {

 // Некоторые действия...

     throw 1; // Выбрасываем исключение

// Инструкции после throw не выполняются!

     std::cout << "Не выполняется";

  }

  catch( int e ) // перехват исключения типа int

  {

     std::cout << "Исключение типа int: " << e << std::endl;

  }

  catch( double e ) // перехват исключения типа double

  {

     std::cout << "Исключение типа double: " << e << std::endl;

  }

  catch( ... ) // перехват всех исключений

  {

     std::cout << "Исключение неизвестного типа!" << std::endl;

  }

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

// Коды ошибок (исключения)

enum ErrorCode

{

  NEGATIVE_ROOT_ERROR, // Ошибка при попытке вычислить квадратный

                       // корень отрицательного аргумента

  ZERO_DIVISION_ERROR  // Ошибка при попытке деления на 0

};

// Функция вычиляет квадратный корень sqrt(val)

double squareRoot( double val )

{

  if ( val < 0.0 )

     throw NEGATIVE_ROOT_ERROR; // Выбрасываем исключение

  return std::sqrt( val );

}

// Функция выполняет деление a / b

double divide( double a, double b )

{

  if ( b == 0 )

     throw NEGATIVE_ROOT_ERROR; // Выбрасываем исключение

  return a / b;

}

// Функция вычисляет sqrt( a / b )

double sqrtDiv( double a, double b )

{

  return squareRoot( divide( a, b ) );

}

void main()

{

  try // Попытка выполнить "опасный" участок кода

  {

     double result = sqrtDiv( 5, -1 );

  }

  catch( ErrorCode errorCode ) // перехват исключений типа ErrorCode

  {

     std::cout << "Ошибка с кодом: " << errorCode << std::endl;

  }

}


 

А также другие работы, которые могут Вас заинтересовать

20539. Уравнение Беллмана для непрерывных процессов 92.5 KB
  Разобьем этот интервал на 2 интервала Рис Где бесконечно малая величена Запишем уравнение 3 на этих 2х отрезках Используя принцип оптимальности: 4 Обозначим через Подставив в 4 Поскольку значение от выбора управления не зависит то ее можем внести под знак минимума и тогда выражение 5 Разделим каждое слагаемое этого уровня на Перейдем к приделу при На основании теоремы о среднем значении интеграла на бесконечно малом отрезке времени Пояснение Рисунок Тогда 5а 6 полная производная этой функции. Вместо Полученное...
20540. Многокритериальные задачи теории принятия решений 31.5 KB
  Проблему решения оптимизационных задач с учетом множества показателей эффективности называют проблемой решения многокритериальных задач или проблемой векторной оптимизации. Формулировка проблемы оптимизации по векторному критерию была в первые сформулирована Вильфредо Парето 1896г. Таким образом проблема векторной оптимизации это проблема принятия компромиссного решения. В настоящие время можно выделить 4 подхода к основной проблеме векторной оптимизации: т.
20541. Множество решений, оптимальных по Парето 153 KB
  Пусть задача принятия решения состоит в максимизации двух противоречивых и не сводимых друг к другу. Кривая АВ определяет для рассматриваемого примера область Парето которая характеризуется тем свойством что любое принадлежащий этой области решения нельзя улучшить одновременно по всем скалярным критерием. Действительно выбрав произвольно точку М в допустимой области решения не лежащую на кривой АВ не трудно убедится что определяемая ее решению можно улучшить по критерию в точке и максимум в точке достигает максимума. Из сказанного...
20542. Основная задача управления 36.5 KB
  Пусть компоненты управления u представляют собой кусочнонепрерывные функции времени с конечным числом точек разрыва или параметрами. Значение вектора управления u принадлежат заданой допустимой области U uU границы которой могут быть функции времени. Задача определения управления гарантирующего выполнения ограничения1 является типичной задачей управления которую назовем ОЗУосновная задача управления.
20543. Геометрическая интерпретация ОЗУ 323.5 KB
  Пусть вектор управления U и вектор функционала J имеет по две компоненты: U=U1 U2; J=J1 J2 Управление принимает свои значения из области U а функционалы J из прямоугольника a1≤J1≤A2; a2≤J2≤A1 Задавая различные управления U1U2 из области U и используя уравнение процесса получим на плоскости функционалов некоторую область В. область U отображается в область В. Пересечение областей А и В это есть область выполнения ограничений при допустимых управлениях U. При заданной области допустимых управлений U реализуется область Au= А∩В...
20544. Методологические основы теории принятия решений. Основные этапы принятия решений 27 KB
  Процесс принятия решения является одним из наиболее сложных .этапы: 1 определить цель принимаемого решения 2 определить возможные решения данной проблемы 3 определить возможные исходы каждого решения 4 оценить каждый исход 5 выбрать оптимальные решения на основе поставленной цели.
20545. Количественный анализ при сбыте продукции 35 KB
  Предполагаемые объемы продаж по ценам: Предполагаемый объем продаж при данной цене Возможная цена за единицу 8 долл. 86 долл. 88 долл.000 Переменный расход 4 долл.
20546. Функция полезности. Определение размеров риска 29.5 KB
  Теория полезности позволяет принимающему решение влиять на результат исходов согласно своим оценкам полезности. Количественно рациональность выбора определяется fей полезности. Теория полезности экспериментально подтверждается в зче о вазах.