|
Программирование >> Полиморфизм без виртуальных функций в с++
Возобновление или завершение? 1ЦВ1НВ1ЕЕ9 применения возобновления были связаны с неумением отделить друг от друга различные уровни абстракции. Мэри Фонтана представила аналогичные данные по системе Explorer компании Texas Instruments, в которой возобновление использовалось только для отладки. Эрон Инсинга (Агоп Insinga) познакомил нас с примерами очень ограниченного и не слишком важного применения семантики возобновления в системе DEC VMS, а Ким Кнутилла (Kim Knutilla) поведал точно такую же историю, как Джим Митчелл, только речь в ней шла о двух больших внутренних проектах IBM, просушествовавших достаточно долго. В пользу завершения свидетельствовал также опыт компании L.M.Ericsson, о котором рассказал Дэг Брюк. Итак, комитет по стандартизации С++ высказался за семантику завершения. 16.6.1. Обходные пути для реализации возобновления Похоже, что большую часть преимушеств, которые способна дать семантика возобновления, можно получить комбинированием вызова функции и исключения (с семантикой завершения). Рассмотрим функцию, которая вызывается для захвата некоторого ресурса X: X* grab X() захватить ресурс X for (;;) { if (can acquire an X) { ... return some X; не удалось захватить ресурс, попробуем возобновить выполнение grab x failed(); Задача функции grab X f ai led () - обеспечить возможность захвата ресурса X. Если она не в состоянии это сделать, то возбуждает исключение: void grab X failed() if (can make X available) { восстановление сделать X доступным return; throw Cannot get X; исключение Это обобшение техники использования функции new handler для обработки нехватки памяти (см. раздел 10.6). У данного приема, конечно, есть много вариантов. Мне больше всего нравится указатель на функцию, которую пользователь может установить сам, чтобы реализовать собственную политику восстановления. Этот метод не обременяет систему сложным механизмом, необходимым для реализации возобновления. Зачастую он даже не оказывает такого негативного влияния на организацию системы, как общая семантика возобновления. 16.7. Асинхронные события Механизм обработки исключений в C-i-i- не предназначен для обработки асинхронных событий, по крайней мере, напрямую: Можно ли применить исключения для обработки, скажем, сигналов? В большинстве систем разработки на С почти наверняка нет. Проблема в том, что в С используются нереентерабельные функции типа malloc. Если прерывание возникает в середине mal 1ос и возбуждает исключение, то нет никакого способа запретить обработчику исключения вызвать malloc еще раз. В компиляторе С++, в котором механизм вызова функций и вся библиотека времени выполнения спроектированы с учетам реентерабельности, сигналы могли бы возбуждать исключения. Но пока такие компиляторы не стали широко доступными (если это вообще когда-нибудь про-иэойдет), мы рекомендуем четка отделять сигналы от исключений. Во многих случаях было бы разумно построить взаимодействие сигналов и исключений так: обработчик сигнала сохраняет где-то информацию, которая регулярно опрашивается некоторой функцией, а уже эта функция можетеозбудить исключение в зависимости оттого, что обнаружено [Koenig, 1990]. Большинство пользователей C/C++, употребляющих исключения, считают, что для создания надежной системы надо на как можно более ранней стадии придумать механизм для отображения асинхронных событий на модель процесса. Если исключения могут возникать в любой момент во время выполнения и если приходится прерывать обработку одного исключения, с тем чтобы уделить внимание другому, то хаоса не избежать. Низкоуровневая система обработки прерываний должна как можно дальше отстоять от обычных программ. Я разделяю данное мнение. Приняв эту точку зрения, вы не будете напрямую использовать исключения, например, для обработки нажатия клавиши DEL и не станете заменять сигналы в ОС UNIX исключениями. В таких случаях некоторая низкоуровневая процедура обработки прерываний должна выполнить минимальную работу и, возможно, отобразить прерывание на обработчик, способное возбудить исключение в некоторой четко определенной точке программы. Отметим, что в соответствии со стандартом С обработчикам сигналов не разрешено вызывать функции, поскольку не гарантируется, что в этот момент работа компьютера в достаточной мере стабильна, чтобы успешно вызвать функцию и вернуться к выполнению программы. Предполагается, что такие низкоуровневые события, как арифметические переполнения и деление на нуль, обрабатываются специализированными низкоуровневыми средствами, а не с помощью исключений. Это делает поведение С++ в области арифметических операций совместимым с другими языками и позволяет избежать проблем, возникающих в компьютерах с конвейерной архитектурой, где такие события, как деление на нуль, асинхронны. Синхронная обработка деления на нуль и т.п. возможна не на всех компьютерах. А там, где она возможна, сброс конвейера, необходимый для того чтобы такие события можно было перехватить до начала нового вычисления, замедляет работу компьютера (иногда на порядок). 16.8. Распроаранение на несколько уровней Есть веские причины для того, чтобы позволить исключению неявно распространяться не далее функции, вызвавшей ту, где оно возникло. Однако для С++ это не подходит: □ имеются миллионы написанных на С++ функций, которые никто не будет модифицировать для обработки исключения или передачи его дальше; □ бессмысленно пытаться сделать каждую функцию барьером для всех ошибок. Лучшей стратегией является та, где обработкой нелокальных ошибок занимаются специально предназначенные для этого интерфейсы; □ в программах, где используются разные языки, нельзя требовать некоего специфического действия от конкретной функции, поскольку она может быть написана на другом языке. Так, допустимо, что С++-функция, возбуждающая исключение, вызывается из функции, написанной на С, которая, в свою очередь, вызвана из функции на С++, готовой это исключение обработать. Первая причина имеет чисто прагматический характер, зато две последние принципиальны: вторая - это утверждение о стратегии проектирования, а третья о предположительных средах использования С++. 16.9. Статическая проверка в С++ разрешено многоуровневое распространение исключений, но из-за этого утрачена одна из возможностей статического контроля. Нельзя, просто посмотрев на код функции, определить, какие исключения она может возбуждать. Фактически функция способна возбуждать любые исключения, даже если в тексте иет ни одного предложения throw, ибо исключения могут возбуждаться вызванными функциями. Несколько программистов, в особенности Майк Пауэлл (Mike Powell), горько жаловались на эту особенность и думали над тем, как для исключений в С++ предоставить более твердые гарантии. Наилучший вариант - каждое возбужденное исключение перехватывается тем или иным обработчиком, написанным пользователем. Также часто требуется гарантия, что покинуть функцию могут только исключения из явно заданного списка. Механизм задания списка исключений, возбуждаемых функцией, в основных чертах спроектировали на доске Майк Пауэлл, Майк Тиман и я примерно в 1989 г. Па существу, запись void f() throw (el, e2) { предложения эквивалентно токой: void f() { try { предложения
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |