Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 90
Текст из файла (страница 90)
Какие дополнительные операции нужны этим спискам по сравнению с односвязными вариантами списков? (*2) Завершите шаблон Яг1ла из 813.2, основанный на классе Яллл из э!1.12. (*2) Определите шаблонную функцию югг(), принимающую критерий сравнения в качестве шаблонного аргумента. Определите класс Лесогон с двумя полями — соиле и рг[се. Выполните сортировку гесгог<11есогг[> по каждому полю.
(*2) Реализуйте шаблон дзогг() . (*2) Напишите программу, которая читает пары (йеу, га!ие), а затем вычис- ляет суммы значений га1ие, соответствующих уникальным значениям яеу. Сформулируйте требования к типам значений га!ве и «еу. (*2.5) Реализуйте простой ассоциативный массив Мар, основанный на классе Агзос из 811.8. Убедитесь, что Мар работает корректно как для С-строк, так и для згг[яя в качестве типа lгеу.
Убедитесь, что Мар работает корректно как для типов с умолчательными конструкторами, так и для типов без оных. Обеспечьте возможность итерации по элементам контейнера Мар. (*3) Сравните производительность программы подсчета слов из 811.8 по сравнению с программой, не использующей ассоциативного массива. В обо- их случаях используйте одинаковый ввод/вывод. (*3) Перепрограммируйте Мар из 813.9[8[, используя более подходящие структуры данных (например, «красно-черные» деревья).
(*2.5) Используйте Мар для выполнения топологической сортировки, опи- санной в [Кпигп,1968) т.! (второе издание), стр. 262. (*1.5) Обеспечьте корректную работу программы суммирования из в13.9[7] для имен, содержащих пробелы. (*2) Напишите шаблоны геаЖие() для разных форматов строк ввода, напри- мер, (1гегп,сопл!,рг!се). 4З2 Глава 13 Шаблоны 14. (*2) Используйте технику, кратко описанную для Айегапие из 513.4 для сортировки строк в обратном лексикографическом порядке. Убедитесь, что указанная техника работает как на системах, где сваг является знаковым типом, так и на системах с беззнаковым сваг.
Напишите вариант программы для сортировки без учета регистра символов. 15. (*1.5) Придумайте пример, в котором демонстрируются, по крайней мере, трн различия между Функциональными шаблонами и макросами (не считая разницы в синтаксисе их определений). 16. (*2) Разработайте схему, гарантирующую, что компилятор проверяет общие ограничения на аргументы шаблонов, для которых создаются объекты. Недостаточно просто проверить ограничения вида «Тдолжно быть типом, производным от Му Ьазе». Обработка исключений Не перебивайте меня, когда я вас перебиваю. — Уинстон Черчилль Обработка ошибок — группировка исключений — перехват исключений — перехват всех исключений — повторная генерация — управление ресурсами— аиго ргг — исключения и пею — исчерпание ресурсов — исключения в конструкторах — исключения в деструкторах — исключения, не являющиеся ошибками — спецификация исключений — неожиданные исключения неперехваченные исключения — исключения и эффективность — альтернативные методы обработки ошибок — стандартные исключения — советы — упражнения.
14.1. Обработка ошибок Как отмечалось в 58.3, автор библиотеки в состоянии обнаружить ошибки времени выполнения, но обычно не имеет представления, что с ними нужно делать. Пользователь библиотеки может знать, как бороться с такими ошибками, но он их не обнаруживает — в противном случае, все было бы сосредоточено в пользовательском коде, а о библиотеке и речь бы не шла. Понятие исключения (ехсергюп) как раз и призвано помочь в разрешении этой коллизии. Центральная идея заключается в том, что функция, обнаружившая ошибку, с которой она не может справиться самостоятельно, генерирует (г)отпев) исключение в надежде, что вызвавшая ее (непосредственно или косвенно) функция сможет обработать возникшую ошибку. Функции, которые имеют намерение решать проблемы такого рода, должны явно указать, что они перехватывают (сагсп) данный тип исключения (ф2.4.2, 88.3).
Такой стиль обработки ошибок предпочтительней многих более традиционных способов. Рассмотрим возможные альтернативы. Обнаружив проблему, с которой невозможно справиться локально, функция может; 434 Глава 14. Обработка исключений 1. Прекратить выполнение. 2. Возвратить значение, указывающее на ошибку. 3. Вернуть некоторое допустимое значение и оставить программу в ошибочном состоянии. 4. Вызвать функцию, специально предназначенную для вызова в случае ошибки. Вариант [1), «прекратить выполнение» вЂ” это то, что происходит по умолчанию, когда исключения не перехватываются. Для большей части ошибок мы можем и должны придумать что-нибудь получше.
В частности, библиотека, которая ничего не знает о цели и общей стратегии использующей ее программы, не может просто так вызвать ех)г() или аЬогг() . Такую библиотеку, неожиданно завершающую работу всей программы, невозможно использовать в составе надежных приложений. Центральный взгляд на обработку исключений (динамически возникающих ошибочных ситуаций) состоит в том, что нужно управление передавать в вызывающую функцию в тех случаях, когда локально невозможно принять решение об обработке ошибок. Вариант [2), «возвратить значение, указывающее на ошибку» — не всегда возможен, ибо часто не существует подходящих значений, указывающих на ошибки. Например, для функции с возвратом типа )яг, любое возвращаемое значение является нормальным, приемлемым результатом. Но даже, если можно выделить специальные «ошибочные возвраты», то все равно такой подход неудобен, поскольку требуется каждый вызов функции проверять на совпадение его возврата с «ошибочными значениями».
Это легко может удвоить объем программного кода (5! 4.8). Как следствие, такой подход редко когда применяют систематически для борьбы со всеми возможными ошибками. Подход [3), «вернуть некоторое допустимое значение и оставить программу в ошибочном состоянии» вЂ” плох тем, что вызывающая функция может не заметить, что программа находится в ненормальном состоянии. Например, многие библиотечные функции языка С для индикации ошибки устанавливают значение глобальной переменной еггяо Я20.4.1, 822.3).
Однако ж, многие программы не проверяют еппо систематически, и в итоге они не застрахованы от последующих ошибок, вызванных использованием значений, полученных от ошибочно отработавших библиотечных функций. Более того, использование глобальных переменных для ищгикации ошибок проблематично при работе нескольких программ в параллельных процессах. Обработка исключений не тождественна случаям, когда уместен вариант [4)— «вызвать функцию, специально предназначенную для вызова в случае ошибки», В отсутствие обработки исключений, вариант [4) имеет те же самые три альтернативы в плане того, как именно специальная функция для обработки ошибок выполняет эту работу. Дальнейшее обсуждение функций обработки ошибок и исключений см. в 514.4.5. Механизм обработка исключений предоставляет альтернативу традиционным методам в случаях, когда последние не достаточны, не элегантны илн сами подвержены ошибкам.
Он формулирует способ явного отделения кода обработки ошибок от регулярного кода программы, что способствует лучшей читаемости программы человеком и большей удобоваримости ее кода для вспомогательных инструментальных средств. Механизм обработки исключений предоставляет более регуляр- ) 4.1. Обработка ошибок 435 ную технологию обработки ошибок, упрощающую взаимодействие между раздельно написанными программными модулями. Один аспект, принятый в схеме обработки исключений, и состоящий в том, что умолчательной реакцией на ошибки (особенно на ошибки в библиотеках) является завершение работы программы, необычен для программистов на языках С или Рааса!.
Для них традиционной реакцией является попытка «тянуть программу далее, надеясь на лучшее». Этот аспект механизма обработки исключений делает использующие его программы более капризными в том смысле, что он требует больших усилий и внимания для того, чтобы довести работу программы до конца. Однако такой подход более предпочтителен, ибо значительно лучше бороться с проблемами на ранних этапах разработки, чем на поздних, и уж тем более нежелательно передавать якобы готовую программу ничего не подозревающему конечному пользователю, чтобы именно он столкнулся с этими проблемами.
Когда завершение работы программы неприемлемо, можно осуществлять перехват всех исключений (В14.3.2) нли некоторой их точно специфицированной части 514.6.2). Таким образом, исключения прекращают выполнение программ только тогда, когда программист позволяет им это делать. Это значительно лучше, чем безоговорочное завершение программ в случаях, когда недостаточная обработка ошибок приводит к их катастрофическому состоянию. Иногда программисты пытаются смягчить последствия подхода «тянуть до конца» путем вывода сообщений об ошибках, отображением диалоговых окон, обращением к пользователю за подмогой и т.д.
Такие подходы действительно полезны при отладке программ, когда пользователем является сам программист, знакомый со структурой программы. Для обычного же пользователя библиотека, запрашивающая помощь у (возможно отсутствующего) пользователя/оператора, неприемлема. Кроме того, в некоторых случаях сообщениям об ошибке просто негде появиться (скажем, если программа исполняется в системах, где поток сегг не подключен к средствам визуализации), но и без этого конечному пользователю они не понятны. Далее, сообщения могут быть на естественном языке, непонятном пользователю (скажем, на финском языке — для пользователя-англичанина).
Что еще хуже„ сообщения об ошибках от библиотек могут касаться ее внутренних реалий, незнакомых пользователю (например, сообщение «недопустимый аргумент для агаи2» в ответ на неправильный ввод для графической системы). Хорошие библиотеки не должны так «разговаривать с пользователями». Исключения позволяют коду, обнаружившему проблему, но не знающему, что с ней делать, отпасовать ее в другую часть системы, которая способна принять правильное решение. И только эта часть системы, которая видит весь контекст исполнения программы, может также и сформировать осмысленное сообщение об ошибке.
Механизм обработки исключений можно рассматривать как аналог механизма выявления ошибок типов и разрешения неоднозначностей на стадии компиляции, только механизм исключений работает на стадии выполнения программы. Обработка исключений делает более важным этап проектирования и может увеличить объем работ, необходимых для получения начальных, работающих с ошибками версий программы. Однако в результате получается конечный код, у которого намного больше шансов работать как задумано, как часть большой программы, понятной другим программистам, и который доступен для автоматической обработки различными инструментальными средствами. Как и другие средства языка С++, обработ- Глава ! 4. Обработка исключений 43б ка исключений предоставляет программисту средства для хорошего стиля программирования, который можно лишь неформально и не в полном объеме практиковать в таких языках как С или Рааса!. Надо понимать, что обработка ошибок является сложной задачей и что механизм обработки исключений, даже с учетом его большей формализации по сравнению с альтернативными методами, все же менее структурирован на фоне иных средств языка, имеющих дело лишь с локальным контекстом потока выполнения программы.
Механизм обработки исключений предоставляет программисту возможность обрабатывать ошибки там, где их обрабатывать наиболее сподручно для конкретной системы. Исключения позволяют явным образом продемонстрировать всю сложность обработки ошибок. Но не они являются первопричиной этой сложности (не следует обвинять гонца с плохими новостями).