Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 97
Текст из файла (страница 97)
Обработка исключений 466 в случае невозможности выполнить штатные вычисления. Причины, по которым полная стратегия зашиты неосуществима, варьируются от программы к программе и от программиста к программисту. Тем не менее, для больших программ типично следующее: 1. Объем работы, обеспечивающий предельную защищенность кода, слишком велик, чтобы его можно было выполнять последовательно и единообразно. 2. Накладные расходы на память и быстродействие неприемлемо велики (вероятна тенденция повторять одни и те же проверки, например корректность аргументов, снова и снова).
3. Функции, написанные на других языках, скорее всего выпадут из установленных правил. 4. Локальное достижение сверхнадежиости приводит к сложностям, от которых страдает надежность совокупной системы. И тем не менее, разбиение программ на подсистемы, которые либо завершаются успешно, либо терпят неудачу определенным образом, очень важно, достижимо и экономично. Так, важная библиотека, подсистема или ключевая функция должны разрабатываться подобным образом.
А спецификации исключений участвуют в формировании интерфейсов к таким библиотекам и подсистемам. Обычно нам не приходится писать код большой системы от самого нуля и до конца. Поэтому, для реализации общей стратегии обработки ошибок во всех частях программы нам придется принять во внимание фрагменты, написанные в соответствии с иными стратегиями. При этом мы столкнемся с большим разнообразием в подходах по управлению ресурсами и вынуждены будет обращать внимание на то, в каком состоянии оставляются фрагменты после обработки ошибок.
Конечная цель состоит в том, чтобы заставить работать фрагмент так, как будто он внешне следует обшей стратегии обработки ошибок (хотя внутренне и подчиняется своей собственной уникальной стратегии). Иногда возникает необходимость в смене стиля обработки ошибок. Например, после вызова традиционной библиотечной функции языка С мы можем проверять переменную еппо и, возможно, генерировать исключение, или же действовать противоположным образом: перехватывать исключение и устанавливать значение еггпо перед возвратом в С-функцию из библиотеки С++: го14 саИС() Ягое (С бтеинт) ( еггио=б; с Гиист)ои (); (Г(еггио) ( У очисткаГс1еапир), если возможна и необходима тягот С а1етгт (егтао); ) ехтегл "С" го)4 саИ /тот С() тагозг () ( тгг 467 14.10.
Стандартные исключения ( с р(ия )Р(ив [ипсйоп ( ); ) сатсЬ (... ) ( (очистка(с!еапир), если возможна и необходима еггпо=Е СРЕРЕ ЯСТВ«.Е)«1 Т; ) ) 14.10. Стандартные исключения Приведем таблицу стандартных исключений, а также функций, операций и общих средств, генерирующих эти исключения: Стандартные исключения (генерируются языком) ' Загол. файл Имя Чем генерируется Ссылки 96.2.6.2, 919.4.5 <печи> Ьт( апас <(уре(п(о> 915.4.1.1 дунет(с саз! Ьт( сае! <туре(п(о> 915.4.4 Ьад туреЫ (уреЫ <ехсер(юп> спецтрикацип исключений 914.6.3 Ьаа ехсерйоп ои! о[ гинее ~ <зтс(ехсерт> ф3.7.2, 516.3.3, 920.3.3 ат() ' <з(с(ехсер1> 917.5.3 Ь(тзе(<>корегатог()() <зтс)ехсер(> ф 1 7.5.3. 1 (пиз(Ы агеитеи! конструктор Бизе! <Мехсерт> 917,5.3.3 921.3.6 Ь(увет<»::(о и!опе() огег[)от еггог <(оз> Ыз Ьаве::[а((иге (оз Ьазекс(еагО Библиотечные исключения являются частью иерархии классов с корневым классом ехсеряол, представленным в файле <ехсерйоа>: В таких случаях важно быть достаточно последовательным, чтобы гарантировать переход на некоторый единый стиль обработки ошибок.
Обработку ошибок следует, насколько это возможно, выполнять иерархически. Если функция наталкивается на ошибочную ситуацию в процессе своего выполнения„ей не следует за помощью обращаться к вызвавшей функции с тем, чтобы та справилась с проблемой восстановления и повторного вьщеления ресурсов — это порождает в системе циклические зависимости. К тому же, это делает программы запутанными и допускает возможность входа в бесконечные циклы обработки ошибок и восстановления. Упрощающие методики, такие как «получение ресурса есть инициализация» и «исключение представляет ошибку» делают код обработки ошибок более регулярным.
В 924.3.7.1 изложены идеи приложения ннвариантов и утверждений (аметбопз) к механизму исключений. 468 Глава 14. Обработка исключений с1аее ехсерйоп ( риЫ1с < ехсер<юп () <Ьгон (); ехсерйоп (сопл< ехсерйопа) <Ьго<г() 1 ехсерйола орега<ос= (сопл< ехсерйопа) <Ьгот() йг<иа< -ехсерйоп () <Ьгот (); ттаиа< солги сйаг* <гйа<() сопл« Ьго<г () рг!гаге: /... ) Графически эта иерархия выглядит следующим образом: ехсерйоп гипйте еггог <оа!с е<тог !ела<Ь ег ог галие еггог йота<и е<то< Ьай ай айос иге<зги<с еггог ои< оГ галле лйегу!о<г еггог ай ехсер<юп !пга!!й агаит <оз Ьазе«уа<1иге Такая организация кажется избыточной для восьми стандартных исключений.
Однако она образует каркас для более широкой совокупности, нежели исключения стандартной библиотеки. Логическими называются ошибки (!оя!с еггог), которые в принципе можно вьивить до начала исполнения программы или путем проверки аргументов функций и конструкторов. Ошибками времени выполнения (гипй'- <ле еггог) являются все остальные ошибки. Многие люди считают представленную схему полезной основой для всех ошибок и исключений. Я так не считаю.
Классы исключений стандартной библиотеки не добавляют функций к набору, предоставляемому корневым классом ехсерйол; они лишь надлежащим образом определяют необходимые виртуальные функции. Так, мы можем написать: го!й.Г() <гу ( // используем стакдартную библиотеку са<сЬ (ехсерйола е) ( сои«< "з<апйагй !<Ьгагу ехсерйоп" « е. тйа<() « ",и'; 14,1 1. Советы 469 сатен (... ) ( сонг « "отоег ексерггон',и"; р ... ) ио(а' реггеггеа' ( ) ггу ( гогою ехсераои () т ) савко (ехсерйон ь е) ( реггегтеа' ( ); сонг «е.юйаг(); ) ) д рекурсивная генерация исключения д рекурсивный вызов функции Цель применения здесь операции вывода состоит единственно в том, чтобы помешать компилятору использовать одну и ту же память под исключения с именем е.
14.11. Советы 1. Используйте исключения для обработки ошибок; 514.1, 914,5, 914.9. 2. Не используйте исключения там, где достаточно локальных управляющих структур; 514.1. 3. Для управления ресурсами используйте стратегию «получение ресурса есть инициализация»; 914.4, 4. Не каждая программа должна безопасно обрабатывать исключения; 914.4.3.
5. Для поддержки инвариантов используйте обработчики исключений и технику «получение ресурса есть инициализация»; 514.3.2. Стандартные исключения являются производными от ехсервол. Но зто неверно для всех исключений вообще, так что будет ошибкой пытаться перехватить все исключения с помощью одного лишь обработчика ехсерпов. Также ошибкой будет полагать, что любое производное от ехсервоп исключение является исключением стандартной библиотеки: программисты могут добавлять свои собственные исключения к иерархии схсерпол. Обратите внимание на то, что операции классов иерархии ехсергтол сами не генерируют исключений.
Из этого, в частности, следует, что генерация исключений стандартной библиотеки не порождает исключения ()аИ айос. Механизм обработки исключений резервирует некоторое количество памяти для своей работы (исключения хранятся, скорее всего, в стеке), так что в принципе можно написать код, который постепенно исчерпает всю память. Например, ниже приведена функция, которая проверяет, кто первый исчерпает всю память — функциональный вызов или механизм обработки исключений: Глава ! 4. Обработка исключений 470 14.12. Упражнения б. 8. 10. 11. 12. 13. 14.
15. 16. 17. 18. 19. 20. 21. Минимизируйте использование ггу-блоков. Вместо применения явного кода обработчиков используйте технику «получение ресурса есть инициализация»; з!4.4. Не в каждой функция нужно обрабатывать все ошибки; 914.9. Для сигнализации об ошибке в конструкторе генерируйте исключение; з14.4.6. Перед генерацией исключения в операции присваивания убедитесь, что опе- ранды находятся в корректном состоянии; 914.4.6.2.
Избегайте генерации исключений нз деструкторов; 914.4.7. Заставляйте функцию та!в(! перехватывать все исключения и сообщать о них; з14.7. Отделяйте обычный код от кода обработки ошибок; 914.4.5, 9 14.5. Перед генерацией исключения в конструкторе убедитесь, что все ранее затре- бованные конструктором ресурсы освобождены; 914.4. Организуйте управление ресурсами иерархически; 914.4. Для важных интерфейсов применяйте спецификацию исключений; 0!4.9. Не забывайте про возможные утечки памяти, выделенной операцией вею, н неосвобожденной в случае генерации исключений; 914.4.1, 914.4.2, З14.4.4.
Предполагайте, что каждое исключение, которое может быть сгенерировано в функции, будет сгенерировано; 914.6. Не предполагайте, что любое исключение наследует от ехсервои; 914.10. Библиотеки не должны безусловно завершать выполнение программ — им следует генерировать исключения и оставлять принятие решения вызываю- щей функции; 914.!. Библиотека не должна выдавать диагностические сообщения„рассчитанные на конечного пользователя. Им нужно генерировать исключение и оставить остальную работу вызывающей функции; в!4.1. Разрабатывайте стратегию обработки ошибок на ранних этапах проектирова- ния; ~14.9.
(*2) Обобщите класс ЯТС (914.6.3. !) до шаблона, который использует страте- гию «получение ресурса есть инициализация» для хранения и восстановления функций разных типов. (*3). Завершите класс Ргг Го Т из 911.11 в виде шаблона, который использует исключения для сигнализации об ошибках времени выполнения. (*3) Напишите функцию, которая выполняет поиск в бинарном дереве с уз- лами, содержащими сваг*.