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

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

Исключительная ситуация (исключение) — это событие при выполнении программы, которое приводит к ее ненормальному или неправильному поведению.

Существует два вида исключений:

  • Аппаратные (структурные, SE-Structured Exception), которые генерируются процессором. К ним относятся, например,
    • деление на 0;
    • выход за границы массива;
    • обращение к невыделенной памяти;
    • переполнение разрядной сетки.
  • Программные, генерируемые операционной системой и прикладными программами – возникают тогда, когда программа их явно инициирует. Когда встречается аномальная ситуация, та часть программы, которая ее обнаружила, может сгенерировать, или возбудить, исключение.

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

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

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

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

Для реализации обработки исключений в C++ используются выражения

  • try,
  • throw,
  • catch.

Блок try {…} позволяет включить один или несколько операторов, которые могут создавать исключение.

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

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

Сразу за блоком try находится блок защищенного раздела кода.

Выражение throw вызывает исключение, т.е. программно создает его.

Блок кода после catch является обработчиком исключения. Он перехватывает исключение, если типы в выражениях throw и catch совместимы.

Если оператор catch задает многоточие (…) вместо типа, то этот блок catch обрабатывает все типы исключений, не имеющие собственных обработчиков (все остальные типы исключений).

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

Как правило, блок catch(…) используется для ведения журнала ошибок и выполнения специальной очистки перед остановкой выполнения программы.

 
 
 
 
 
 
 
 
 
 
try 
{ … // защищенный раздел кода
  throw параметр;
}
catch (параметр) // обработка исключения
{
}
catch (…)  // обработка остальных исключений
{
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
int main()
{
  try
  {
    cout << "Exception: ";
    throw 1;
    cout << "No exception!";
  }
  catch (int a)
  {
    cout << a;
  }
  cin.get(); 
  return 0;
}

Исключение throw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
int main() 
{
  try 
  {
    cout << "Exception: ";
    //throw 1;
    cout << "No exception!";
  } 
  catch (int a) 
  {
    cout << a;
  }
  cin.get(); 
  return 0;
}

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

Рассмотрим пример программы, генерирующей исключительную ситуацию «деление на 0».

1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;
int main() 
{
  int a = 0, b =10;
  cout << b/a << endl;
  cin.get();
  return 0;
}

При попытке запустить программу на выполнение видим следующее:

Для обработки исключительной ситуации необходимо операцию деления поместить в блок защищенного кода:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
int main() 
{
    int a = 0, b = 10;
    try 
    {
        cout << b / a << endl;
    }
    catch (...)
    {
        cout << "error";
    }
    cin.get();
    return 0;
}

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

И для пункта меню C/C++ → Создание кода → Включить C++ исключения устанавливаем значение Да, с SEH исключениями (/EHa).

При запуске программы на выполнение имеем следующий результат:

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

4 комментария к “Обработка исключительных ситуаций”

  1. Виктор

    Хороший сайт. Наконец-то преподаватели начали учить чему-то интересному (эх, а в наши-то студенческие они нас боялись 🙂 ). Но по теме написано мутновато. "Выражение throw … означает, что исключительное условие произошло в блоке try." — если б не знал, что это такое, то ничего б не понял.
    Я бы написал так:

    Блок try { } предназначен, чтобы выполнить определенную в нем часть программы и перехватить исключения, которые могут при этом возникнуть.
    Следующий за ним блок catch (…) { } будет выполнен в случае возникновения исключения в блоке try. Если исключения не возникнет, то блок catch будет пропущен.
    Выражение throw exception; внутри блока try прерывает обычное выполнение программы и немедленно передает управление в соответствующий блок catch.

  2. Лучший сайт для студентов как минимум.  Все понятно и доходчиво, желательно бы еще раздел о потоках в с++. Спасибо Вам за вашу работу!

  3. Блог программиста

    Хотел написать у себя статью про обработку исключений, но слишком сложно. Я не осилил.

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

    — приведение типа (throw 1; catch(float) или наоборот);

    — передачу исключений по значению, ссылку и указателю;

    — спецификации исключений.

    Реально вопрос очень сложный, так например у Маерса половина книжки посвящена этому вопросу. Но Маерс писал книжки давно и много внимания освятил спецификациям, но с тех пор, как вышел C++11 на MSDN можно найти статью, в которой пишут, что в современном С++ спецификации использовать не желательно.

    Тем не менее, внимание этому (в статье) уделить все равно надо, т.к. спецификации из языка никто не убрал и в других языках (например Java) с ними никаких проблем нет. Т.е. пока что не очень очевидно как пользоваться всем этим в С++ (Маерс тоже писал про это, типа «разработчики компиляторов сами имеют недостаточный опыт работы с исключениями, поэтому …»).

    В общем, тема скользкая — я уже больше двух лет не могу решиться написать статью по этой теме )

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх