|
Программирование >> Оптимизация возвращаемого значения
значению создается копия передаваемого объекта, которая и передается функции. То же самое происходит при передаче исключения по значению. Таким образом, если определить аргумент catch следующим образом: catch {Widget W) ... Перехват по значению. придется создавать две копии объекта-исключения: одна для временного объекта, генерируемого для всех исключений, и вторая - для помещения этого временного значения в w. Аналогично, при перехвате исключения по ссылке: catch (Widget&w) Перехватпо ссылке. catch(const Widget&w) ... Такжеперехватпоссылке. придется учитывать создание копии исключения, необходимой для временного объекта. Если же функция передается аргументу по ссылке, копирования не происходит. Таким образом, при генерации исключения приходится создавать (и затем удалять) на одну копию объекта-исключения больше, чем при передаче этого объекта в качестве аргумента функции. До сих пор не рассматривалась генерация исключения через указатель, но этот случай эквивалентен передаче аргумента через указатель. И в том, и в другом случае передается копия указателя. Главное не использовать указатель для исключения, которое было создано как локальный объект, потому что локальный объект будет удален, когда исключение покинет его область видимости. В этом случае блоку catch передается указатель на уже удаленный объект. Именно для таких ситуаций и придумано правило обязательного копирования. То, что жизненный цикл объектов равен времени от точки вызова функции или оператора throw до передачи аргументов или блока catch, составляет первое отличие передачи параметров от распространения исключений. Второе отличие состоит в правилах сопоставления типа, которое осуществляют, с одной стороны, вызывающий модуль или генератор исключения, а с другой - вызываемая функция или перехватчик исключения. Взгляните на функцию sqrt из стандартной математической библиотеки: double sqrt (double) ; Функция из <cmath. h> или <math. h> . Квадратный корень из целого числа можно извлечь следующим образом: int i ; double sqrtOf i = sqrt (i) ; Здесь нет ничего удивительного. Язык допускает неявное преобразование из типа int в double, поэтому при вызове функции sqrt переменная i по умолчанию преобразуется к типу double и результат выполнения sqrt относится к этому параметру double. (См. правило 5, где более полно обсуждаются операции неявного преобразования типа). Но, как правило, при соотнесении типа исключения с типом аргумента catch такие преобразования не выполняются. В следующем примере: void f ( int value) { try { if (someFunction()) { throw value; } catch(doubled){ Если someFunction() возвращает true, генерируем / / исключение типа int. Здесьдолжны обрабатываться / / исключения типа double. возникшее в блоке try исключение типа int никогда не будет перехвачено оператором catch, который требует аргумент типа double. Этот оператор перехватывает только исключения типа double, никакие преобразования типа не производятся. Следовательно, если нужно перехватить исключение типа int, то необходимо использовать другой оператор catch (динамически включаемый), принимающий int или int& (возможно, с атрибутами const или volatile) в качестве типа исключения. Тем не менее, существуют два вида преобразований, которые могут применяться при сопоставлении типов в операторах catch. Первый вид - это приведение наследуемых типов. Оператору catch, перехватывающему исключения типа базового класса, разрешено перехватывать также исключения типа производного класса. Рассмотрим, например, иерархию исключений для обработки ошибок, определенную в стандартной библиотеке С++ (см. рис. 3.2). Оператор catch, перехватывающий исключения типа runtime error, может также перехватывать исключения типа range error, а оператор catch, перехватывающий исключения базового класса exception, - вообще любое исключение из этой иерархии. Это правило преобразования наследуемых исключений действует по отношению к значениям, ссылкам и указателям по следующему общему принципу: catch (runtime error) . . . catch(runtime error&) ... catch(construntime error&) catch (runtime error*) ... catch(const runtiem error*) / / Также перехватывает исключения типа runtime error, range error или overflow error. / / Также перехватывает исключения типа runtime error*, range error* или overflow error*. invalld argument Рис. 3.2 Второй вид разрешает преобразование из типизированного в нетипизированный указатель, так что оператор catch, имеющий в качестве аргумента указатель const void*, перехватывает все исключения, имеющие тип произвольного указателя: catch (constvoid*) / / Перехватывает все / / исключения типа указатель . Наконец, последнее различие между передачей аргумента и распространением исключения состоит в том, что операторы catch всегда обслуживаются в порядке их перечисления. Таким образом, исключение типа производного класса может быть обработано оператором catch для базового класса, даже если блок catch для производного класса и соответствующий блок try находятся в одном модуле. Например: try { catch(logic error&ex) { } catch (invalid argument&ex) { / / Этот блок перехватит не / / только все исключения типа logic error, / / но и наследующие от них. / / Этот блок никогда не получит управление, / / потому что все исключения типа invalid argument будут перехвачены предыдущим блоком.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |