Меню Закрыть

Герб саттер решение сложных задач на с

Содержание

Библиотека программиста запись закреплена

Решение сложных задач на С++ (2015,pdf)
Автор: Герб Саттер

В данном издании объединены две широко известные профессионалам в области программирования на C++ книги Герба Саттера Exceptional C++ и More Exceptional C++ , входящие в серию книг C++ In-Depth, редактором которой является Бьерн Страуструп, создатель языка C++. Материал этой книги составляют переработанные задачи серии Guru of the Week, рассчитанные на читателя с достаточно глубоким знанием C++, однако книга будет полезна каждому, кто хочет углубить свои знания в этой области.

Данная книга представляет собой продолжение вышедшей ранее книги Решение сложных задач на C++.
В форме задач и их решений рассматриваются современные методы проектирования и программирования на C++. В книге сконцентрирован богатый многолетний опыт программирования на C++ не только самого автора, но и всего сообщества программистов на C++, так что некоторые рекомендации автора могут показаться неожиданными даже опытным программистам-профессионалам.
Автор рассматривает и конкретные методики, приемы и идиомы программирования, однако основная тема книги — это стиль программирования, причем в самом широком понимании этого слова.
Особое внимание во всех задачах книги уделено вопросу проектирования, которое должно обеспечить максимальную надежность, безопасность, производительность и сопровождаемость создаваемого программного обеспечения. Книга рассчитана в первую очередь на профессиональных программистов с глубокими знаниями языка, однако она будет полезна любому, кто захочет углубить свои знания в данной области.

Если вам понравилась эта книга поделитесь ею с друзьями, тем самым вы помогаете нам развиваться и добавлять всё больше интересных и нужным вам книг!

April 7, 2015

Все мы знаем, а многие даже читали, книгу Герба Саттера “Решение сложных задач на С++”, которая появилась благодаря известным публикациям из серии под названием “Guru of the Week”. Одна из задач в этой книге была посвящена std::uncaught_exception и звучала она так:

GotW #47: ”Что собой представляет стандартная функция std::uncaught_exception и когда она должна использоваться?

Читайте также:  Бесплатный аналог офиса для windows 10

Ответ дан в книге Саттера еще в далеком 2002 году. Однако, комитет решил вернуться к обсуждению этой “редкой” функции в 2013. Что же решили изменить и для чего все-таки нужна эта функция, я постараюсь “за 5 минут” рассказать в данном посте. Уделите время, и Вы также узнаете про декларативный подход в обработке ошибок, предложенный Александреску.

Бесполезная функция

Стандартная функция std::uncaught_exception() позволяет понять, не является ли в настоящее время активным какое-то исключение. Эта функция возвращает булевое значение, которое равно true в том случае, если в момент вызова std::uncaught_exception() происходит раскрутка стека в данном потоке.

Как часто она применяется на практике? Скорее всего очень редко, возможно, что и никогда. В своей книге Саттер, после нескольких показательных примеров, говорит следующее:

“К сожалению, я не знаю ни одного полезного и безопасного применения функции std::uncaught_exception(). Мой совет: не используйте её!” GotW #47

Казалось бы, что после таких слов про эту функцию можно было забыть навсегда. Однако, в 2013 году Саттер вновь возвращается к ней, и даже пишет предложение в комитет под номером N3614 (и уточнение N4152), в котором предлагается эту функцию заменить… Очень показательно то, что сам Саттер “не забывал” про std::uncaught_exception(), хотя и советовал не использовать эту функцию. Зачем же снова обсуждать её, и пытаться что-то улучшить в новом стандарте С++17?

ScopeGuard

Функция std::uncaught_exception снова попала в поле зрения во многом благодаря Андрею Александреску. Саттер пишет, что именно его примеры послужили главным мотивом к пересмотру возможностей uncaught_exception. Речь идет о реализации класса ScopeGuard, который предложил Александреску. Вот ссылка на его лекцию, которая называется “Error Handling in C++”. Она довольно большая и подробная, я же постараюсь “в двух словах” рассказать, зачем нужен ScopeGuard.

ScopeGuard — это шаблон класс, который позволяет выполнить какие-либо действия в рамках определенной области, в том случае если, в данной области произошло исключение, и в случае, если исключения не произошло. Если Вы знакомы с языком Python или Java, то знаете про оператор finally, который позволяет выполнить блок инструкций в любом случае, было ли исключение, или нет. Язык С++ не поддерживает finally, однако у нас есть идиома RAII. Деструктор локальной переменной будет вызван при выходе её из области видимости, и в случае возникновения исключения. А значит мы сможем сделать те действия, которые “поместили” бы в блок finally, будь он в нашем арсенале. Но всё не так просто…

Читайте также:  Как доказать неравенство 9 класс

Что если действия, которые требуется выполнить, различны для случая, когда было возбуждено исключение, и для случая “нормального” выполнения кода (без исключений). Например, нам требуется реализовать “rollback” в случае возникновения исключения, то есть откат всех изменений, внесенных с определенной точки. Вот тут-то нам и помогла бы uncaught_exception:

Всё замечательно и просто, за исключением того, что… этот код не будет правильно работать вот в каком случае:

Во время выполнения программы возникло исключение ( throw 1 ). Ничего страшного в этом нет, и мы готовы его обработать. Но перед этим, в процессе раскрутки стека, будут вызваны деструкторы локальных объектов, в том числе и foo. В деструкторе foo вызывается функция LogStuff, чтобы всю информацию внести в базу данных. Для этого создается объект класса Transaction, и выполняются все необходимые действия. Обратите внимание, что функция LogStuff отрабатывает без ошибок, а значит данные должны попасть в базу. Но! Когда вызывается деструктор объекта Transaction (по выходу из функции LogStuff), uncaught_exception() вернет true. Так как мы находимся в процессе раскрутки стека, и есть активное исключение. И все наши данные лога будут “откатаны” и потеряны. И хотя проблем с LogStuff никаких не случилось, наша транзакция среагировала на “внешнее” исключение, в то время когда, нас интересовали только исключения с момента создания объекта Transaction и до момента его уничтожения. Другими словами, важно не то — было ли исключение вообще, а то — было ли исключение в определенной области видимости. Именно этот пример приводит Саттер в своем письме комитету.

Поэтому ScopeGuard нельзя написать используя uncaught_exception. Ведь уже название подчеркивает, что Guard “охраняет” определенный “Scope”, а не распространяется на всю программу.

Читайте также:  Milwaukee m18 2606 20

Александреску

В настоящее время в стандартной библиотеке С++ (включая стандарт С++14) нет ничего, что помогло бы отличить успешное выполнение кода в определенной области видимости, от ситуации, когда именно в этой области возникло исключение.

Однако, Андрей Александреску придумал, как реализовать ScopeGuard: он предложил ввести функцию int getUncaughtExceptionCount(), которая возвращала бы сколько исключений активно в данный момент (т.е. возбуждено, но не обработано). Если мы будем располагать такой информацией, то нам удастся реализовать настоящий ScopeGuard. Вот решение от Александреску:

Александреску указал, что реализовать функцию int getUncaughtExceptionCount() сегодня возможно на всех основных компиляторах С++. А Саттер, помня о схожей (но не удачной) функции uncaught_exception, предложил переименовать getUncaughtExceptionCount() в std::uncaught_exceptions, просто добавив в конце “s”. И вот результат:

В С++17 появится функция std::uncaught_exceptions, в замен “старой” std::uncaught_exception. Эта функция позволит реализовывать классы вида ScopeGuard, которые в свою очередь позволят обрабатывать ошибки в декларативном виде, не используя явно try/catch во многих ситуациях. Примеры такого подхода Вы сможете найти в N4152. В конце этого документа приложена презентация Андрея Александреску. Изучая примеры использования, начинаешь понимать, что нам может дать std::uncaught_exceptions. Вот самый простой случай:

Примеров здесь можно привести очень много, например, с вложенными блоками try/catch, которые можно переписать с использованием ScopeGuard в намного более понятной форме. И всё это, есть продолжение идиомы RAII, которую все мы очень любим. Поэтому комитет принял предложение ввести uncaught_exceptions, взамен “старой” uncaught_exception. Благодаря стараниям Саттера и Александреску.

Рекомендуем к прочтению

Добавить комментарий

Ваш адрес email не будет опубликован.