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

  Юрий  | 

    | 

  Обновл. 21 Мар 2019  | 

 1216

Иногда вы можете столкнуться с ситуацией, когда нужно поймать исключение, но обрабатывать его в данный момент времени вы не хотите (или не имеете возможности). Например, вы можете записать ошибку в лог-файл, а затем передать её обратно в 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 (13 оценок, среднее: 4,69 из 5)
Загрузка...

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

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