Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [ 25 ] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

Проблема первая - призраки типов

3. Является ли спецификация исключений частью типа функции? Обоснуйте свой ответ.

Джон Спайсер (John Spicer) из знаменитой Edison Design Group, автор большой части главы стандарта С++, посвященной шаблонам, назвал спецификации исключений С++ "призрачными типами" (shadow type). Одна из важнейших характеристик С++ - строгая типизация, и это хорошо и правильно. Но почему же мы называем спецификации исключений призрачными типами, а не частью системы типов С++?

Причина проста, хотя и имеет "двойное дно":

• спецификации исключений не являются частью типов функций;

• за исключением тех ситуаций, когда они являются частью типов.

Рассмотрим сначала пример, когда спецификации исключений не участвуют в образовании типа функции.

пример 13-3(а): спецификацию исключений нельзя

использовать в инструкции typedef.

void f() throw(A,в);

typedef void (*pf)() throw(A,B); ошибка

PF pf = f; невозможно из-за ошибки

Спецификация исключений не может использоваться в определении типа посредством typedef. С++ не позволит вам написать такой код, так что спецификации исключений не могут участвовать в типе функции... как минимум, в контексте typedef. Но в других случаях спецификации исключений в действительности участвуют в типах функций, если их записать без использования typedef.

Пример 13-3(6): все то же, но без typedef!

void fО throw(A,в);

void (*pf)() throw(A,B); ok

pf = f; ok

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

Пример 13-3(в): тоже вполне кошерно и с низким содержанием холестерина.. . :)

void fО throw(A,В);

void C*pf)() throw(A,B,c); ok

pf - f; ok, тип pf менее строгий

Спецификации исключений также участвуют в типах виртуальных функций, когда вы пытаетесь их перекрыть.

пример 13-3(г): спецификации исключений имеют значение

для виртуальных функций.

class С {

virtual void f() throw(A,в); некоторая спецификация

исключений

class D : С {

void fС); ошибка - спецификация

исключений менее строгая

Итак, первая проблема, связанная со спецификациями исключений, состоит в том, что в сегодняшнем С++ они являются "призрачными типами", которые играют по правилам, отличным от обычных правил системы типов С++.



Проблема вторая -- (не)понимание

Вторая проблема - следует точно знать, что получается при использовании спецификации исключений.

4. Что собой представляют спецификации исключений и как они работают? Дайте точный ответ на поставленный вопрос.

Вот как обычно профаммисты представляют себе работу спецификаций исключений.

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

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

Эти ожидания, увы, несколько обманчивы. Рассмотрим еще раз код примера 13-2(6).

пример 13-2(6): два потенциальных обмана

int GuncO throwO; не генерирует исключений < -?

int HuncO throw(A,B); может генерировать только А или в <-?

Корректны ли приведенные комментарии? Не совсем. Функция Gunc на самом деле может сгенерировать исключение, а функция nunc - сгенерировать исключение, отличное от А и в! Компилятор только гарантирует, что если это произойдет - он просто, не дрогнув, прикончит вашу программу.

Раз функции Gunc и Nunc могут сгенерировать все, что угодно, а не только обещанное, то компилятор не только не может полагаться на то, что этого не произойдет, но должен выполнять еще и роль полицейского, следя за тем, что именно генерируется в этих функциях и насколько оно соотносится с обещаниями, данными в спецификациях исключений. Если все же обещание будет нарушено, компилятор должен вызвать функцию unexpected, что в большинстве случаев приведет к завершению работы вашей профаммы. Почему? Потому что из этой функции есть только два пути, и ни один из них не является нормальным возвратом из функции. Выбирайте сами.

• Можно сгенерировать исключение, которое разрешено спецификацией исключений. В этом случае распространение исключения продолжится как обычно. Но помните, что обработчик unexpected - глобальный, т.е. он один-единственный на всю программу. Вряд ли такой глобальный обработчик окажется достаточно разумным, чтобы выполнить нечто разумное в каждом частном случае, так что, скорее всего, путь один - вызов terminate...

• Можно сгенерировать исключение, которое спецификацией исключений не разрешено. Если в спецификации исключений исходной функции имеется исключение bad exception, то далее будет распространяться именно оно. Ну, а если нет, то путь один - вызов terminate...

Поскольку нарушение спецификации исключений в подавляющем большинстве случаев приводит к завершению работы вашей профаммы, я думаю, о таком поведении вполне можно сказать, что "нарушение, не дрогнув, прикончит вашу профамму".

В начале ответа на четвертый вопрос мы видели, чего программисты ожидают от спецификаций исключений. Давайте внесем коррективы в эти ожидания.

• Гарантируют- во время выполнения программы заставляют что функции будут генерировать только перечисленные исключения (возможно, не будут генерировать их вообще).

• Позволяют или запрещают компилятору выполнить определенную оптимизацию, основанную на знании о том,~что ыотут быть сгенерированы-тодысо перо



численные исключопия проверке, имеется ли сгенерированное исключение в списке спецификации исключений.

Чтобы понять, как должен работать компилятор, рассмотрим следующий код, в котором приведено тело одной из рассматриваемых функций - нипс.

Пример 13-4(а)

int HuncO throw(A,в) { return juncO ;

Компилятор должен сгенерировать код наподобие приведенного ниже, и расходы процессорного времени на его обработку точно такие же, как если бы вы ввели его самостоятельно (единственное облегчение - вам не надо набирать сго самостоятельно; компилятор сам позаботится о его генерации).

пример 13-4(6): обработанный компилятором

код из примера 13-4(а)

int Hunc() try {

return 3unc();

catchC A ) { throw;

catchC в ) { throw;

catchC ... ) {

std::unexpected C); отсюда нет возврата! в лучшем

случае здесь будет сгенерировано исключение А или В

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

Копнем поглубже

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

Есть как минимум еще две причины снижения производительности программы из-за спецификаций исключений.

• Некоторые компиляторы автоматически делают невстраивасмыми функции, объявленные как inline, если у них имеются спецификации исключений. Это такая же эвристика, как и отказ некоторых компиляторов во встраиваемости функциям, которые имеют слишком много вложенных инструкций или содержащим циклы.

• Некоторые компиляторы вообще не в состоянии оптимизировать механизм исключений и добавляют автоматически сгенерированные try/catch-блоки даже к функциям, тела которых не генерируют исключений.

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



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [ 25 ] 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85