Обработка исключений в C ++ ограничена попыткой / throw / catch. В отличие от Object Pascal, Java, C # и Python, даже в C ++ 11 finally
конструкция не была реализована.
Я видел очень много литературы по С ++, обсуждающей «код, исключающий исключение». Липпман пишет, что безопасный код исключений - это важная, но сложная и трудная тема, выходящая за рамки его учебника, из которого следует, что безопасный код не является фундаментальным для C ++. Херб Саттер посвящает 10 глав этой теме в своем «Исключительном C ++»!
Тем не менее, мне кажется, что многие проблемы, возникающие при попытке написать «безопасный код для исключения», могли бы быть достаточно хорошо решены, если бы finally
была реализована конструкция, позволяющая программисту гарантировать, что даже в случае исключения программа может быть восстановлена. в безопасное, стабильное состояние без утечек, близкое к точке выделения ресурсов и потенциально проблемного кода. Как очень опытный программист на Delphi и C #, я использую try ... finally довольно широко блокирует мой код, как и большинство программистов на этих языках.
Рассматривая все «навороты», реализованные в C ++ 11, я был удивлен, обнаружив, что «наконец-то» все еще не было.
Итак, почему finally
конструкция никогда не была реализована в C ++? Это действительно не очень сложная или продвинутая концепция для понимания, и она помогает программисту писать «безопасный для исключений код».
источник
finally
C ++, и какие методы обработки исключений используются вместо него?» действительно и по теме для этого сайта. Думаю, существующие ответы хорошо это освещают. Превращая это в дискуссию на тему «Есть ли причины, по которым дизайнеры C ++ не включают в себяfinally
смысл?» и "Долженfinally
быть добавлен в C ++?" и ведение дискуссии через комментарии по вопросу, и каждый ответ не соответствует модели этого сайта вопросов и ответов.Ответы:
В качестве дополнительного комментария к ответу @ Nemanja (который, поскольку он цитирует Страуструпа, на самом деле такой же хороший ответ, как вы можете получить):
Это просто вопрос понимания философии и идиом языка C ++. Возьмите пример операции, которая открывает соединение с базой данных в постоянном классе и должна убедиться, что оно закрывает это соединение, если выдается исключение. Это вопрос безопасности исключений и применяется к любому языку с исключениями (C ++, C #, Delphi ...).
На языке, который использует
try
/finally
, код может выглядеть примерно так:Просто и понятно. Однако есть несколько недостатков:
finally
блок, иначе я пропускаю ресурсы.DoRiskyOperation
это больше, чем один вызов метода - если у меня есть некоторая обработка вtry
блоке - тогдаClose
операция может закончиться тем, что она будет немного дальше отOpen
операции. Я не могу написать мою уборку прямо рядом с моим приобретением.try
/finally
.Подход C ++ будет выглядеть так:
Это полностью решает все недостатки
finally
подхода. У него есть пара собственных недостатков, но они относительно незначительны:ScopedDatabaseConnection
класс самостоятельно. Однако это очень простая реализация - всего 4 или 5 строк кода.Лично, учитывая эти преимущества и недостатки, я считаю RAII гораздо более предпочтительным методом
finally
. Ваш пробег может варьироваться.Наконец, поскольку RAII является такой устоявшейся идиомой в C ++, и для того, чтобы облегчить разработчикам часть бремени написания многочисленных
Scoped...
классов, существуют библиотеки, такие как ScopeGuard и Boost.ScopeExit, которые способствуют такой детерминированной очистке.источник
using
оператор, который автоматически очищает любой объект, реализующийIDisposable
интерфейс. Так что, хотя это возможно сделать неправильно, довольно легко понять это правильно.try/finally
конструкцией, потому что компилятор не предоставляетtry/finally
конструкцию, и единственный способ получить к ней доступ - через основанный на классе идиома дизайна, не является «преимуществом»; это само определение инверсии абстракции.finally
. Как я уже сказал, ваш пробег может отличаться.От Почему C ++ не предоставляет конструкцию "finally"? в FAQ по стилю и технике С ++ Бьярна Страуструпа :
источник
finally
конструкция всегда бесполезна навсегда, несмотря на то , что говорит Струсуп. Тот факт, что написание «безопасного кода исключений» является большой проблемой в C ++, является доказательством этого. Черт, в C # есть и деструкторыfinally
, и они оба привыкли.Причина, по которой C ++ не имеет,
finally
заключается в том, что он не нужен в C ++.finally
используется для выполнения некоторого кода независимо от того, произошло ли исключение или нет, что почти всегда является своего рода кодом очистки. В C ++ этот код очистки должен находиться в деструкторе соответствующего класса, и деструктор всегда будет вызываться, какfinally
блок. Фраза использования деструктора для вашей очистки называется RAII .В сообществе C ++ может быть больше разговоров о «безопасном от исключений» коде, но он почти одинаково важен в других языках, в которых есть исключения. Весь смысл «безопасного для исключений» кода состоит в том, что вы думаете о том, в каком состоянии ваш код остается, если исключение происходит в любой из функций / методов, которые вы вызываете.
В C ++ «безопасный для исключения» код немного важнее, потому что C ++ не имеет автоматической сборки мусора, которая заботится об объектах, оставшихся сиротами из-за исключения.
Причина, по которой безопасность исключений обсуждается более подробно в сообществе C ++, вероятно, также связана с тем фактом, что в C ++ вам нужно больше понимать, что может пойти не так, потому что в языке меньше сетей безопасности по умолчанию.
источник
finally
в стандарт C ++, я думаю, можно с уверенностью сделать вывод, что сообщество C ++ не считаетthe absence of finally
проблему. У большинства языков, в которых естьfinally
C ++ , отсутствует последовательное детерминированное разрушение. Я вижу, что у Delphi есть их оба, но я не знаю его историю достаточно хорошо, чтобы знать, что было там первым.finally
». Я никогда не могу вспомнить ни одной задачи, которая бы облегчила бы, если бы у меня был к ней доступ.Другие обсуждали RAII как решение. Это совершенно хорошее решение. Но на самом деле это не решает, почему они не добавили,
finally
так как это очень популярная вещь. Ответ на этот вопрос является более фундаментальным для проектирования и разработки C ++: на протяжении всей разработки C ++ те, кто участвовал в этом, решительно сопротивлялись внедрению конструктивных особенностей, которые могут быть достигнуты с использованием других функций без особой суеты, и особенно там, где это требует введения. новых ключевых слов, которые могут сделать старый код несовместимым. Поскольку RAII предоставляет высокофункциональную альтернативу,finally
и выfinally
в любом случае можете использовать свой собственный язык в C ++ 11, это было мало чем нужно.Все, что вам нужно сделать, это создать класс,
Finally
который вызывает функцию, переданную ее конструктору в его деструкторе. Тогда вы можете сделать это:Однако большинство нативных программистов на C ++ предпочитают чисто спроектированные объекты RAII.
источник
Finally atEnd([&] () { database.close(); });
Кроме того , я представляю следующее лучше:{ Finally atEnd(...); try {...} catch(e) {...} }
(я поднял финализатор из-за примерки блока , так что выполняется после улова блоков.)Вы можете использовать шаблон «trap» - даже если вы не хотите использовать блок try / catch.
Поместите простой объект в требуемую область. В деструкторе этого объекта заложена ваша "финальная" логика. Независимо от того, что, когда стек размотан, будет вызван деструктор объекта, и вы получите свою конфету.
источник
Ну, вы можете сделать что-то по-своему
finally
, используя Lambdas, который получит следующее для правильной компиляции (используя пример без RAII, конечно, не самый хороший кусок кода):Смотрите эту статью .
источник
Я не уверен, что согласен с утверждениями, что RAII является надмножеством
finally
. Ахиллесова пята RAII проста: исключения. RAII реализован с помощью деструкторов, и в C ++ всегда неправильно выбрасывать деструктор. Это означает, что вы не можете использовать RAII, когда вам нужен код очистки для выброса. Сfinally
другой стороны, если бы они были реализованы, нет никаких оснований полагать, что было бы незаконно бросать изfinally
блока.Рассмотрим путь к коду:
Если бы мы имели,
finally
мы могли бы написать:Но я не могу найти способ получить эквивалентное поведение, используя RAII.
Если кто-то знает, как это сделать в C ++, я очень заинтересован в ответе. Я даже был бы счастлив с чем-то, на что полагалось, например, с обеспечением того, чтобы все исключения наследовались от одного класса с какими-то особыми возможностями или чем-то еще.
источник
complex_cleanup
можете бросить, вы можете иметь случай, когда два неперехваченных исключения находятся в полете одновременно, так же, как вы это делали бы с RAII / деструкторами, а C ++ отказывается это допустить. Если вы хотите, чтобы исходное исключение было замечено,complex_cleanup
следует исключить любые исключения, как это было бы с RAII / деструкторами. Если вы хотите, чтобыcomplex_cleanup
исключение было видно, то я думаю, что вы можете использовать вложенные блоки try / catch - хотя это касательно и трудно вписать в комментарий, так что стоит отдельного вопроса.finally
блоке будет работать точно так же, как и бросок вcatch
блоке WRT, исключение в полете - не вызовstd::terminate
. Вопрос "почему нетfinally
в C ++?" и ответы все говорят: "тебе это не нужно ... RAII FTW!" Я хочу сказать, что да, RAII подходит для простых случаев, таких как управление памятью, но до тех пор, пока проблема исключений не будет решена, для решения общего назначения требуется слишком много размышлений / накладных расходов / беспокойства / перепроектирования.