Динамо-машины  Метод Сократа 

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 86 87 88 89 90

Задача 13. Прагматичный взгляд

на спецификации исключений Сложность: 6

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

Вопрос для новичка

1. Что произойдет при нарушении спецификации исключений? Почему? Каковы основные причины существования этой возможности С++?

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

int FuncC);

int GuncQ throwO ;

int HuncC) throw(A,B);

Вопрос для профессионала

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

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

5. Когда стоит использовать спецификацию исключений в функции? Почему вы используете (или не используете) эту возможность?

Решение

Как вы знаете, сейчас идет работа над новым стандартом С++ (рабочее название С++Ох). Давайте оглянемся назад и постараемся осмыслить накопленный опыт работы с текущим стандартом [С++03]. Подавляющее большинство стандартных возмож-ностей С++ просто замечательны, и именно им посвящена львиная доля публикаций, что НС удивительно - кому хочется твердить о недостатках! Слабые, мало используемые возможности языка чаше всего просто игнорируются и постепенно забываются (и это далеко не всегда плохо). Вот почему встречается очень мало статей о таких невразумительных возможностях языка, как vaiarray, bi tset и прочих - и в их число входят и спецификации исключений.

Давайте поближе познакомимся с имеющимся опытом использования стандартных спецификаций исключений С+ + .

Нарушение спецификации

1. Что произойдет при нарушении спецификации исключений? Почему? Каковы основные причины существования этой возможности С++?

Идея спецификаций исключений заключается в проверке времени выполнения того, что данная функция может генерировать только определенные типы исключений (либо не генерировать их вовсе). Например, приведенная ниже спецификация исключений гарантирует, что f может генерировать только исключения типа л или в *:

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



int fC) throwС A, в );

Если будет сгенерировано исключение, которого нет в списке спецификации исключений, будет вызвана функция unexpectedC).

Пример 13-1

int f() throw(A,B) { А и В не связаны с С

throw с(); Будет вызвана функция unexpected

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

void MyUnexpectedhandlerO { /* ... */ } std::set unexpected( AMyUnexpectedhandler );

Остается один вопрос - что может делать ваш обработчик? Единственное, чего он не может делать, - это выйти из функции при помощи оператора return. Поэтому у него есть два варианта действий:

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

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

Применение

Идея, лежащая в основе спецификаций исключений, очень проста: в программе С++ любая функция, если не указано иное, может генерировать исключения любого типа. Рассмотрим некоторую функцию Func.

2. Какие исключения мотуг быть сгенерированы каждой из перечисленных ниже функций. Пример 13-2(а)

int FuncО; Может генерировать любые исключения

По умолчанию в С++ функция Func может генерировать исключения любого типа, как сказано в комментарии к ней. Однако зачастую нам известно, что функция может генерировать только исключения определенных типов. В таком случае может оказаться разумным сообщить об этом компилятору и программисту. Например;

Пример 13-2(6)

int GuncQ throwO; не генерирует исключений

int HuncО throw(А,в); может генерировать только А или в

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

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



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

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(b): тоже вполне кошерно и с низким содержанием холестерина.. . :)

void fО throw(A,В);

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

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

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

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

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

class С {

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

исключений

class d : С {

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

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

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



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 86 87 88 89 90