Урок №186. Повторная генерация исключений

  Юрий  | 

  |

  Обновл. 15 Сен 2021  | 

 18178

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

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

Это легко выполнимая задача при использовании кодов возврата, например:

В примере, приведенном выше, функции поручено создать объект класса Database, открыть базу данных, а затем возвратить объект класса Database обратно. В таком случае, если что-то пойдет не так (например, передастся неправильный filename), то обработчик исключений запишет ошибку в лог-файл, а затем возвратит нулевой указатель.

Теперь рассмотрим следующую функцию:

В случае успеха выполнения этой функции возвращается целочисленное значение.

Но что, если что-то пойдет не так с getIntValue()? В таком случае, функция getIntValue() сгенерирует целочисленное исключение, которое будет перехвачено блоком catch в getIntValueFromDatabase(), который запишет ошибку в лог-файл. Но как мы затем сообщим caller-у функции getIntValueFromDatabase(), что что-то пошло не так? В отличие от функции из первого примера, здесь нет хорошего кода возврата, который мы могли бы использовать (поскольку функция getIntValueFromDatabase() возвращает целочисленное значение, то любое целочисленное значение в качестве кода возврата является допустимым).

Генерация нового исключения


Одним из очевидных решений является генерация нового исключения:

В примере, приведенном выше, программа ловит исключение типа int из getIntValue(), записывает ошибку в лог-файл, а затем выбрасывает новое исключение со значением q типа char. Хотя генерация исключения в блоке catch может показаться странной затеей, это не запрещено. Помните, что только исключения, сгенерированные в блоке try, могут быть перехвачены блоком catch. Это означает, что исключение, сгенерированное в блоке catch, не будет перехвачено блоком catch, в котором оно находится. Вместо этого стек начнет раскручиваться, и исключение будет передано caller-у, который находится на уровне выше в стеке вызовов.

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

Неправильная повторная генерация исключений

Альтернативным решением является повторная генерация одного и того же исключения:

Хотя это работает, но здесь есть пара нюансов. Во-первых, в блоке catch не генерируется точно такое же исключение, которое обрабатывает блок catch, а генерируется копия переменной exception. Этот вариант плох тем, что снижает производительность (незначительно, но можно было бы и без этого обойтись).

Рассмотрим, что произойдет в следующем случае:

Здесь функция getIntValue() выбрасывает объект класса Child в качестве исключения, но блок catch принимает по ссылке объект класса Parent. Это нормально, так как мы уже узнали на уроке №162, что ссылка родительского класса может использоваться для указания на дочерний объект. Однако в таком случае копия exception является класса Parent, а не класса Child! Другими словами, произойдет обрезка объекта класса Child()!

Мы можем это увидеть в следующей программе:

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

Caught Parent p, which is actually a Child
Caught Parent p, which is actually a Parent

Тот факт, что вторая строка указывает на то, что Parent на самом деле является Parent, а не Child, доказывает, что произошла обрезка объекта Child.

Правильная повторная генерация исключений


К счастью, язык C++ предоставляет способ повторной генерации одного и того же исключения. Для этого нужно просто использовать ключевое слово throw внутри блока catch без указания какого-либо идентификатора. Например:

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

Caught Parent p, which is actually a Child
Caught Parent p, which is actually a Child

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

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

Правило: При повторной генерации исключения используйте ключевое слово throw без указания какого-либо идентификатора.

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

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

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

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