Урок №182. Обработка исключений. Операторы throw, try и catch

  Юрий Ворон  | 

    | 

  Обновлено 27 Ноя 2018  | 

 433

 ǀ   2 

В предыдущем уроке мы говорили о необходимости и пользе исключений. Исключения в C++ реализованы с помощью трёх ключевых слов, которые работают в связке друг с другом: throwtry и catch.

Выбрасывание исключений

Мы постоянно используем сигналы в реальной жизни для обозначения того, что произошли определенные события. Например, во время игры в баскетбол, если игрок совершил серьезный фол, арбитр свистит, и игра останавливается. Затем идёт пенальти. Как только пенальти выполнено, игра возобновляется.

В C++ оператор throw используется, чтобы сигнализировать о возникновении исключения или ошибки (аналогия – арбитр свистит). Сигнализирование о том, что произошло исключение, также обычно называется «генерацией исключения».

Для использования оператора throw используется ключевое слово throw, за которым указывается значение любого типа данных, которое вы хотите использовать, чтобы сигнализировать об ошибке. Как правило, этим значением является код ошибки, описание проблемы или настраиваемый класс-исключение (класс Exception).



Например:

Каждая из этих строк сигнализирует о том, что возникла какая-то ошибка, которую нужно обработать.

Ищем исключения

Выбрасывание исключений — это только одна часть процесса обработки исключений. Вернемся к нашей аналогии с баскетболом: как только арбитр свистнул, что происходит дальше? Игроки останавливаются, и игра временно прекращается. Обычный поток выполнения игры нарушен.

В C++ мы используем ключевое слово try для определения блока стейтментов (так называемый «блок try»). Блок try действует как наблюдатель, ища исключения, которые были выброшены каким-либо из операторов в этом же блоке try.

Например:

Обратите внимание, блок try не определяет, КАК мы будем обрабатывать исключение. Он просто сообщает компилятору: «Эй, если какой-либо из стейтментов внутри этого блока try выбросит исключение – слови его!».

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

Пока арбитр не объявит о пенальти, и пока это пенальти не будет выполнено – игра не возобновится. Другими словами, пенальти должно быть обработано до возобновления игры.

Фактически, обработка исключений — это работа блока(ов) catch. Ключевое слово catch используется для определения блока кода (так называемого «блока catch»), который обрабатывает исключения определенного типа данных.

Вот пример блока catch, который обрабатывает (ловит) исключения типа int:

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

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

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

Как и в случае с функциями, если параметр не используется в блоке catch, то имя переменной можно не указывать:

Это предотвратит вывод предупреждений компилятора о неиспользуемых переменных.

throw, try и catch вместе

Вот полная программа, которая использует throw, try и несколько блоков catch:

Результат выполнения программы выше:

We caught an int exception with value -1
Continuing our way!



Оператор throw используется для генерации исключения -1 типа int. Затем блок try обнаруживает оператор throw и перемещает его в соответствующий блок catch, который обрабатывает исключения типа int. Блок catch типа int и выводит соответствующее сообщение об ошибке.

После обработки исключения, программа продолжает своё выполнение и выводит на экран «Continuing our way!».

Резюмируем

Обработка исключений на самом деле довольно проста, и всё, что вам нужно запомнить, размещено в следующих двух абзацах:

   При выбрасывании исключения (оператор throw), точка выполнения программы немедленно переходит к ближайшему блоку try. Если какой-либо из обработчиков catch, прикрепленных к блоку try, обрабатывает этот тип исключения, то точка выполнения переходит в этот обработчик и, после выполнения кода блока catch, исключение считается обработанным.

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

Обратите внимание, компилятор не выполняет неявные преобразования при сопоставлении исключений с блоками catch! Например, исключение типа char не будет обрабатываться блоком catch типа int, исключение типа int, в свою очередь, не будет обрабатываться блоком catch типа float.

Это действительно всё, что вам нужно запомнить. Остальная часть этой главы будет посвящена демонстрации этих принципов на практике.

Исключения обрабатываются немедленно

Вот маленькая программа, которая демонстрирует, что исключения обрабатываются немедленно:

Рассмотрим выполнение программы пошагово:

   Оператор throw — это первый оператор, который выполняется. Это приводит к генерации исключения типа double.

   Точка выполнения немедленно переходит к ближайшему блоку try, который является единственным блоком try в этой программе (внутри которого и размещено это исключение).

   Затем проверяются обработчики catch на соответствие типу данных. Поскольку в нас исключение типа double, то компилятор ищет обработчик catch типа double. У нас такой есть, поэтому он и выполняется.

Следовательно, результат выполнения этой программы:

We caught a double of value: 7.4

Обратите внимание, строчка «This never prints» никогда не выводится, так как генерация исключения заставило точку выполнения программы немедленно перейти к обработчику исключений типа double.

Еще один пример

Рассмотрим более популярный пример:

Здесь мы просим пользователя ввести число. Если пользователь ввёл положительное число, то стейтмент if не выполняется, исключение не генерируется, и пользователь получает квадратный корень из числа. Поскольку исключение не генерируется, то код внутри блока catch никогда не выполняется. Результат:

Enter a number: 16
The sqrt of 16 is 4

Если же пользователь ввёл отрицательное число, то генерируется исключение типа const char*. Поскольку мы уже находимся в блоке try, то компилятор ищет соответствующий обработчик catch типа const char* и точка выполнения немедленно перемещается в этот блок. Результат:

Enter a number: -3
Error: Can not take sqrt of negative number

Что обычно делают блоки catch?

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

   Во-первых, блок catch может вывести сообщение об ошибке (либо в консоль, либо в лог файл).

   Во-вторых, блок catch может возвратить значение или код ошибки обратно в caller.

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

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

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (5 оценок, среднее: 5,00 из 5)
Загрузка...

Комментариев: 2

  1. Евгений:

    Здравствуйте, сайт очень качественный, но могли бы вы добавить "Непереведенные статьи", выделенные серым цветом, что-бы знать что ты изучишь следующим?

    1. Юрий Ворон Юрий Ворон:

      Привет, хорошая идея. Подумаю.

Добавить комментарий

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