Анимация
JavaScript
|
Главная Библионтека Проблема первая - призраки типов 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 |