Как уже упоминалось, при передаче исключения осуществляется выход из всех блоков по цепочке вызовов, пока не будет найден соответствующий обработчик. При выходе из блока вся память, используемая его локальными объектами, освобождается. В результате передача указателя на локальный объект почти наверняка будет ошибкой. Причина этой ошибки та же, что и у ошибки возвращения из функции указателя на локальный объект (см. раздел 6.3.2). Если указатель указывает на объект в блоке, выход из которого осуществляется перед обработчиком, то этот локальный объект будет удален до обработчика.
При передаче исключения его выражение определяет статический тип (тип времени компиляции) (см. раздел 15.2.3) объекта исключения. Этот момент важно иметь в виду, поскольку большинство приложений передают исключения, тип которых исходит из иерархии наследования. Если выражение throw
обращается к значению указателя на тип базового класса и этот указатель указывает на объект производного класса, то переданный объект отсекается (см. раздел 15.2.3) и передается только часть базового класса.
Передача указателя требует, чтобы объект, на который указывает указатель, существовал на момент выполнения соответствующего обработчика.
Упражнение 18.1. Каков тип объекта исключения в следующих операторах throw
?
(a) range_error r("error"); (b) exception *p = &r
throw r; throw *p;
Что было бы, будь оператор throw
в случае (b) написан как throw p
?
Упражнение 18.2. Объясните, что случится, если исключение произойдет в указанном месте:
void exercise(int *b, int *e) {
vector
int *p = new int[v.size()];
ifstream in("ints");
//
}
Упражнение 18.3. Существуют два способа исправить предыдущий код. Опишите и реализуйте их.
catch
(catch clause) выглядит как список параметров функции, только с одним параметром. Как и в списке параметров, имя параметра обработчика можно пропустить, если у блока catch
нет необходимости в доступе к переданному исключению.
Тип объявления определяет виды исключений, обрабатываемых обработчиком. Тип должен быть завершенным (см. раздел 7.3.3). Тип может быть ссылкой на l-значение, но не ссылкой на r-значение (см. раздел 13.6.1).
При входе в блок catch
параметр в объявлении исключения инициализируется объектом исключения. Подобно параметру функции, если тип параметра обработчика не является ссылочным, параметр обработчика копирует объект исключения; изменения, внесенные в параметр в обработчике, осуществляются с его локальной копией, а не с самим объектом исключения. Если параметр имеет ссылочный тип, то, как любой ссылочный параметр, параметр обработчика будет только другим именем объекта исключения. Изменения, внесенные в ссылочный параметр, осуществляются с самим объектом исключения.
Подобно объявлению параметра функции, параметр обработчика, имеющий тип базового класса, может быть инициализирован объектом исключения типа производного класса. Если у параметра обработчика будет не ссылочный тип, то объект исключения будет отсечен (см. раздел 15.2.3), как и при передаче такого объекта обычной функции по значению. С другой стороны, если параметр является ссылкой на тип базового класса, то параметр будет связан с объектом исключения обычным способом.
Также, подобно параметрам функции, статический тип объявления исключения определяет действия, которые может выполнить обработчик. Если у параметра обработчика будет тип базового класса, то обработчик не сможет использовать члены, определенные в производном классе.
Обычно обработчики, получающие исключения типа, связанного наследственными отношениями, определяют свой параметр как ссылку.
Блок catch
, найденный в ходе поиска соответствующего обработчика, не обязательно является наиболее подходящим данному исключению. В результате исключение будет обработано catch
, который сможет это сделать. Как следствие, в списке директив catch
наиболее специализированные обработчики следует располагать в начале.
Поскольку поиск директивы catch
осуществляется в порядке их объявления, при использовании исключений из иерархии наследования блоки catch
для обработки исключений производного типа следует располагать перед обработчиком для исключения базового типа.
Правила поиска соответствующего исключению блока catch
значительно жестче, чем правила поиска аргументов, соответствующих типам параметров. Большинство преобразований здесь недопустимо — тип исключения должен точно соответствовать обработчику, допустимо лишь несколько различий.