Главная » Просмотр файлов » Бьерн Страуструп

Бьерн Страуструп (947334), страница 53

Файл №947334 Бьерн Страуструп (Стpаустpуп - Книга о C++) 53 страницаБьерн Страуструп (947334) страница 532013-09-15СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 53)

// используем m

}

Этот пример можно записать так:

class Empty { } // тип особой ситуации "Пустая_очередь"

class queue {

// ...

message* get(); // запустить Empty, если очередь пуста

// ...

};

void f2(queue& q)

{

try {

message* m = q.get();

// используем m

}

catch (Empty) { // очередь пуста

// ...

}

}

В варианте с особой ситуацией есть даже какая-то прелесть. Это

хороший пример того, когда трудно сказать, можно ли считать

такую ситуацию ошибкой. Если очередь не должна быть пустой

(т.е. она бывает пустой очень редко, скажем один раз из тысячи),

и действия в случае пустой очереди можно рассматривать как

восстановление, то в функции f2() взгляд на особую ситуацию будет

такой, которого мы до сих пор и придерживались (т.е. обработка особых

ситуаций есть обработка ошибок). Если очередь часто бывает пустой,

а принимаемые в этом случае действия образуют одну из ветвей

нормального хода программы, то придется отказаться от такого взгляда

на особую ситуацию, а функцию f2() надо переписать:

class queue {

// ...

message* get(); // запустить Empty, если очередь пуста

int empty();

// ...

};

void f3(queue& q)

{

if (q.empty()) { // очередь пуста

// ...

}

else {

message* m = q.get();

// используем m

}

}

Отметим, что вынести из функции get() проверку очереди на пустоту

можно только при условии, что к очереди нет параллельных обращений.

Не так то просто отказаться от взгляда, что обработка особой

ситуации есть обработка ошибки. Пока мы придерживаемся такой точки

зрения, программа четко подразделяется на две части: обычная часть

и часть обработки ошибок. Такая программа более понятна. К сожалению,

в реальных задачах провести четкое разделение невозможно, поэтому

структура программы должна (и будет) отражать этот факт. Допустим,

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

get() используется в цикле, и пустота очереди говорит о конце цикла).

Тогда пустота очереди не является чем-то странным или ошибочным.

Поэтому, используя для обозначения конца очереди особую ситуацию,

мы расширяем представление об особых ситуациях как ошибках. С другой

стороны, действия, принимаемые в случае пустой очереди, явно отличаются

от действий, принимаемых в ходе цикла (т.е. в обычном случае).

Механизм особых ситуаций является менее структурированным,

чем такие локальные структуры управления как операторы if или for.

Обычно он к тому же является не столь эффективным, если особая

ситуация действительно возникла. Поэтому особые ситуации следует

использовать только в том случае, когда нет хорошего решения с более

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

Например, в случае пустой очереди можно прекрасно использовать для

сигнализации об этом значение, а именно нулевое значение указателя на

строку message, значит особая ситуация здесь не нужна. Однако, если бы

из класса queue мы получали вместо указателя значение типа int, то

то могло не найтись такого значения, обозначающего пустую очередь.

В таком случае функция get() становится эквивалентной операции

индексации из $$9.1, и более привлекательно представлять пустую очередь

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

в самом общем шаблоне типа для очереди придется для обозначения пустой

очереди использовать особую ситуацию, а работающая с очередью функция

будет такой:

void f(Queue<X>& q)

{

try {

for (;;) { // ``бесконечный цикл''

// прерываемый особой ситуацией

X m = q.get();

// ...

}

}

catch (Queue<X>::Empty) {

return;

}

}

Если приведенный цикл выполняется тысячи раз, то он, по всей

видимости, будет более эффективным, чем обычный цикл с проверкой

условия пустоты очереди. Если же он выполняется только несколько

раз, то обычный цикл почти наверняка эффективней.

В очереди общего вида особая ситуация используется как способ

возврата из функции get(). Использование особых ситуаций как способа

возврата может быть элегантным способом завершения функций поиска.

Особенно это подходит для рекурсивных функций поиска в дереве. Однако,

применяя особые ситуации для таких целей, легко перейти грань разумного

и получить маловразумительную программу. Все-таки всюду, где это

действительно оправдано, надо придерживаться той точки зрения, что

обработка особой ситуации есть обработка ошибки. Обработка ошибок по

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

методы, которые дают ясное представление ошибок в языке и способ

их обработки.

9.6 Задание интерфейса

Запуск или перехват особой ситуации отражается на взаимоотношениях

функций. Поэтому имеет смысл задавать в описании функции множество

особых ситуаций, которые она может запустить:

void f(int a) throw (x2, x3, x4);

В этом описании указано, что f() может запустить особые ситуации

x2, x3 и x4, а также ситуации всех производных от них типов, но

больше никакие ситуации она не запускает. Если функция перечисляет свои

особые ситуации, то она дает определенную гарантию всякой вызывающей ее

функции, а именно, если попытается запустить иную особую ситуацию, то

это приведет к вызову функции unexpected().

Стандартное предназначение unexpected() состоит в вызове функции

terminate(), которая, в свою очередь, обычно вызывает abort().

Подробности даны в $$9.7.

По сути определение

void f() throw (x2, x3, x4)

{

// какие-то операторы

}

эквивалентно такому определению

void f()

{

try {

// какие-то операторы

}

catch (x2) { // повторный запуск

throw;

}

catch (x3) { // повторный запуск

throw;

}

catch (x4) { // повторный запуск

throw;

}

catch (...) {

unexpected();

}

}

Преимущество явного задания особых ситуаций функции в ее описании

перед эквивалентным способом, когда происходит проверка на

особые ситуации в теле функции, не только в более краткой записи.

Главное здесь в том, что описание функции входит в ее интерфейс,

который видим для всех вызывающих функций. С другой стороны,

определение функции может и не быть универсально доступным.

Даже если у вас есть исходные тексты всех библиотечных функций,

обычно желание изучать их возникает не часто.

Если в описании функции не указаны ее особые ситуации, считается,

что она может запустить любую особую ситуацию.

int f(); // может запустить любую особую ситуацию

Если функция не будет запускать никаких особых ситуаций, ее можно

описать, явно указав пустой список:

int g() throw (); // не запускает никаких особых ситуаций

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

никаких особых ситуаций. Но тогда пришлось бы описывать свои особые

ситуации практически для каждой функции Это, как правило,

требовало бы ее перетрансляции, а кроме того препятствовало бы общению

с функциями, написанными на других языках. В результате программист

стал бы стремиться отключить механизм особых ситуаций и писал бы

излишние операторы, чтобы обойти их. Пользователь считал бы такие

программы надежными, поскольку мог не заметить подмены, но это было

бы совершенно неоправдано.

9.6.1 Неожиданные особые ситуации

Если к описанию особых ситуаций относиться не достаточно серьезно, то

результатом может быть вызов unexpected(), что нежелательно во всех

случая, кроме отладки. Избежать вызова unexpected() можно, если хорошо

организовать структуру особых ситуации и описание интерфейса. С

другой стороны, вызов unexpected() можно перехватить и сделать его

безвредным.

Если компонент Y хорошо разработан, все его особые ситуации

могут быть только производными одного класса, скажем Yerr. Поэтому,

если есть описание

class someYerr : public Yerr { /* ... */ };

то функция, описанная как

void f() throw (Xerr, Yerr, IOerr);

будет передавать любую особую ситуацию типа Yerr вызывающей функции.

В частности, обработка особой ситуации типа someYerr в f() сведется к

передаче ее вызывающей f() функции.

Бывает случаи, когда окончание программы при появлении

неожиданной особой ситуации является слишком строгим решением.

Допустим функция g() написана для несетевого режима в распределенной

системе. Естественно, в g() ничего неизвестно об особых ситуациях,

связанных с сетью, поэтому при появлении любой из них вызывается

unexpected(). Значит для использования g() в распределенной системе

нужно предоставить обработчик сетевых особых ситуаций или переписать

g(). Если допустить, что переписать g() невозможно или нежелательно,

проблему можно решить, переопределив действие функции unexpected().

Для этого служит функция set_unexpected(). Вначале мы определим

класс, который позволит нам применить для функций unexpected()

метод "запроса ресурсов путем инициализации" :

typedef void(*PFV)();

PFV set_unexpected(PFV);

class STC { // класс для сохранения и восстановления

PFV old; // функций unexpected()

public:

STC(PFV f) { old = set_unexpected(f); }

~STC() { set_unexpected(old); }

};

Теперь мы определим функцию, которая должна в нашем примере заменить

unexpected():

void rethrow() { throw; } // перезапуск всех сетевых

// особых ситуаций

Наконец, можно дать вариант функции g(), предназначенный для работы

в сетевом режиме:

void networked_g()

{

STC xx(&rethrow); // теперь unexpected() вызывает rethrow()

g();

}

В предыдущем разделе было показано, что unexpected() потенциально

вызывается из обработчика catch (...). Значит в нашем случае

обязательно произойдет повторный запуск особой ситуации. Повторный

запуск, когда особая ситуация не запускалась, приводит к вызову

terminate(). Поскольку обработчик catch (...) находится вне той

области видимости, в которой была запущена сетевая особая ситуация,

бесконечный цикл возникнуть не может.

Есть еще одно, довольно опасное, решение, когда на неожиданную

особую ситуацию просто "закрывают глаза":

void muddle_on() { cerr << "не замечаем особой ситуации\n"; }

// ...

STC xx(&muddle_on); // теперь действие unexpected() сводится

// просто к печати сообщения

Такое переопределение действия unexpected() позволяет нормально

вернуться из функции, обнаружившей неожиданную особую ситуацию.

Несмотря на свою очевидную опасность, это решение используется.

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

системы и отлаживать другие ее части. Такой подход может быть

полезен в процессе отладки и развития системы, перенесенной с языка

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

если ошибки проявляются как можно раньше.

Возможно другое решение, когда вызов unexpected() преобразуется

в запуск особой ситуации Fail (неудача):

void fail() { throw Fail; }

// ...

STC yy(&fail);

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

возможном результате вызываемой функции: эта функции завершится

либо успешно (т.е. возвратится нормально), либо неудачно (т.е.

запустит Fail). Очевидный недостаток этого решения в том, что

не учитывается дополнительная информация, которая может сопровождать

особую ситуацию. Впрочем, при необходимости ее можно учесть, если

передавать информацию вместе с Fail.

9.7 Неперехваченные особые ситуации

Если особая ситуация запущена и не перехвачена, то вызывается функция

terminate(). Она же вызывается, когда система поддержки особых

Характеристики

Тип файла
Документ
Размер
4,26 Mb
Тип материала
Учебное заведение
Неизвестно

Список файлов книги

Свежие статьи
Популярно сейчас
Как Вы думаете, сколько людей до Вас делали точно такое же задание? 99% студентов выполняют точно такие же задания, как и их предшественники год назад. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6367
Авторов
на СтудИзбе
310
Средний доход
с одного платного файла
Обучение Подробнее