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

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

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

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

  • Аппаратные (структурные, 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 и создаваемые системой или приложением асинхронные исключения, например нарушения, связанные с защитой памяти, делением на ноль и числами с плавающей запятой.

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