Поддерживает ли C ++ блоки finally ?
Что такое идиома RAII ?
В чем разница между C ++ идиомой RAII и C # 'использованием' выражения ?
Поддерживает ли C ++ блоки finally ?
Что такое идиома RAII ?
В чем разница между C ++ идиомой RAII и C # 'использованием' выражения ?
Ответы:
Нет, C ++ не поддерживает блоки finally. Причина в том, что C ++ вместо этого поддерживает RAII: «Приобретение ресурсов - это инициализация» - плохое название † для действительно полезной концепции.
Идея состоит в том, что деструктор объекта отвечает за освобождение ресурсов. Когда объект имеет автоматическую продолжительность хранения, деструктор объекта будет вызываться при выходе из блока, в котором он был создан, даже когда этот блок выходит при наличии исключения. Вот объяснение Бьярне Страуструпа по теме.
Обычное использование RAII - блокировка мьютекса:
RAII также упрощает использование объектов в качестве членов других классов. При уничтожении класса-владельца ресурс, управляемый классом RAII, освобождается, потому что в результате вызывается деструктор для класса, управляемого RAII. Это означает, что когда вы используете RAII для всех членов класса, которые управляют ресурсами, вы можете обойтись без очень простого, может даже стандартного деструктора для класса владельца, так как ему не нужно вручную управлять временем жизни его члена. , (Благодаря Майку Б за указание на это.)
Для тех, кто знаком с C # или VB.NET, вы можете признать, что RAII похож на детерминированное уничтожение .NET с использованием операторов IDisposable и «using» . Действительно, два метода очень похожи. Основное отличие состоит в том, что RAII детерминистически освобождает любой тип ресурса, включая память. При реализации IDisposable в .NET (даже на языке .NET C ++ / CLI) ресурсы будут освобождаться детерминистически, за исключением памяти. В .NET память не освобождается детерминистически; память освобождается только во время циклов сбора мусора.
† Некоторые люди считают, что «Разрушение - это отказ от ресурсов» - более точное название идиомы RAII.
источник
В C ++, наконец, НЕ требуется из-за RAII.
RAII переносит ответственность за безопасность исключений от пользователя объекта к разработчику (и исполнителю) объекта. Я бы сказал, что это правильное место, так как вам нужно только один раз получить правильную безопасность исключений (при разработке / реализации). Используя, наконец, вам нужно корректировать безопасность исключений каждый раз, когда вы используете объект.
Также ИМО код выглядит аккуратнее (см. Ниже).
Пример:
Объект базы данных. Чтобы убедиться, что соединение с БД используется, оно должно быть открыто и закрыто. С помощью RAII это можно сделать в конструкторе / деструкторе.
C ++, как RAII
Использование RAII делает использование объекта БД корректно очень простым. Объект БД будет корректно закрываться при использовании деструктора, независимо от того, как мы пытаемся его использовать.
Java, как наконец
При использовании наконец правильное использование объекта делегируется пользователю объекта. т.е. пользователь объекта обязан правильно закрыть соединение с БД. Теперь вы можете утверждать, что это можно сделать в финализаторе, но ресурсы могут иметь ограниченную доступность или другие ограничения, и, таким образом, вы, как правило, хотите контролировать освобождение объекта и не полагаться на недетерминированное поведение сборщика мусора.
Также это простой пример.
Когда у вас есть несколько ресурсов, которые должны быть освобождены, код может усложниться.
Более подробный анализ можно найти здесь: http://accu.org/index.php/journals/236
источник
// Make sure not to throw exception if one is already propagating.
Для деструкторов C ++ важно не выбрасывать исключения также по этой самой причине.RAII обычно лучше, но вы можете легко иметь окончательную семантику в C ++. Используя небольшое количество кода.
Кроме того, основные принципы C ++ дают наконец.
Вот ссылка на реализацию GSL Microsoft и ссылка на реализацию Мартина Моена
Бьярн Страуструп несколько раз говорил, что все, что есть в GSL, в конечном итоге означает перейти в стандарт. Так что это должен быть ориентированный на будущее способ использовать наконец .
Вы можете легко реализовать себя, если хотите, продолжайте читать.
В C ++ 11 RAII и лямбда-выражения позволяют сделать генерал окончательно:
пример использования:
вывод будет:
Лично я использовал это несколько раз, чтобы обеспечить закрытие дескриптора файла POSIX в программе на C ++.
Обычно лучше иметь реальный класс, который управляет ресурсами и, таким образом, избегает любых утечек, но это, наконец , полезно в тех случаях, когда создание класса звучит как перебор.
Кроме того, мне это нравится больше, чем другим языкам, в конце концов, потому что при естественном использовании вы пишете закрывающий код рядом с открывающим кодом (в моем примере new и delete ), а разрушение следует за конструированием в порядке LIFO, как обычно в C ++. Единственным недостатком является то, что вы получаете автоматическую переменную, которую вы на самом деле не используете, а лямбда-синтаксис делает ее немного шумной (в моем примере в четвертой строке только слово finally и {} -блок справа имеют смысл, остальное по сути шум).
Другой пример:
Элемент disable полезен, если оператор finally должен вызываться только в случае сбоя. Например, вам нужно скопировать объект в три разных контейнера, вы можете настроить функцию finally для отмены каждой копии и отключения после того, как все копии будут успешными. Поступая так, если уничтожение не может бросить, вы гарантируете сильную гарантию.
отключить пример:
Если вы не можете использовать C ++ 11, у вас все еще может быть наконец , но код становится немного длиннее. Просто определите структуру только с помощью конструктора и деструктора, конструктор берет ссылки на все, что нужно, а деструктор выполняет необходимые действия. Это в основном то, что делает лямбда, делается вручную.
источник
FinalAction
это в основном то же самое, что и популярнаяScopeGuard
идиома, только с другим именем.Помимо упрощения очистки с помощью стековых объектов, RAII также полезен, поскольку такая же «автоматическая» очистка происходит, когда объект является членом другого класса. Когда класс-владелец уничтожается, ресурс, управляемый классом RAII, очищается, потому что в результате вызывается dtor для этого класса.
Это означает, что когда вы достигаете нирваны RAII, и все члены в классе используют RAII (например, умные указатели), вы можете получить очень простой (возможно, даже стандартный) dtor для класса владельца, так как ему не нужно вручную управлять его время жизни ресурса участника.
источник
На самом деле, языки, основанные на сборщиках мусора, нуждаются в «наконец-то» больше. Сборщик мусора не уничтожает ваши объекты своевременно, поэтому на него нельзя полагаться для правильного устранения проблем, не связанных с памятью.
Что касается динамически распределяемых данных, многие утверждают, что вы должны использовать умные указатели.
Тем не мение...
К сожалению, это его собственный недостаток. Старые привычки программирования на С сильно умирают. Когда вы используете библиотеку, написанную на C или в стиле C, RAII не будет использоваться. Если не считать переписывания всего API-интерфейса, то именно с этим вам и придется работать. Тогда отсутствие «наконец» действительно кусает.
источник
CleanupFailedException
. Есть ли какой-нибудь вероятный способ достичь такого результата с помощью RAII?SomeObject.DoSomething()
метод и хочет знать, успешно ли он (1), (2) не получен без побочных эффектов , (3) - с побочными эффектами, с которыми готовый справиться вызов или (4) не удалось с побочными эффектами, с которыми вызывающий абонент не может справиться. Только звонящий узнает, с какими ситуациями он может и не может справиться; то, что нужно звонящему, это способ узнать ситуацию. Жаль, что нет стандартного механизма предоставления наиболее важной информации об исключении.Еще одна эмуляция блока finally с использованием лямбда-функций C ++ 11
Будем надеяться, что компилятор оптимизирует код выше.
Теперь мы можем написать код так:
Если вы хотите, вы можете обернуть эту идиому в макрос «попробуй - наконец»:
Теперь блок "finally" доступен в C ++ 11:
Лично мне не нравится «макро» версия идиомы finally, и я бы предпочел использовать чистую функцию with_finally, хотя синтаксис в этом случае более громоздкий.
Вы можете проверить код выше здесь: http://coliru.stacked-crooked.com/a/1d88f64cb27b3813
PS
Если вам нужен блок finally в вашем коде, тогда ограниченные области действия или ON_FINALLY / ON_EXCEPTION макросы будут , вероятно , лучше соответствовать вашим потребностям.
Вот краткий пример использования ON_FINALLY / ON_EXCEPTION:
источник
Извините, что выкопал такой старый поток, но в следующих рассуждениях есть серьезная ошибка:
Чаще всего вам приходится иметь дело с динамически размещаемыми объектами, динамическим числом объектов и т. Д. В блоке try некоторый код может создавать множество объектов (количество которых определяется во время выполнения) и сохранять указатели на них в списке. Теперь это не экзотический сценарий, но очень распространенный. В этом случае вы хотели бы написать что-то вроде
Конечно, сам список будет уничтожен при выходе из области видимости, но это не приведет к очистке созданных вами временных объектов.
Вместо этого вы должны идти по безобразному пути:
Кроме того: почему даже управляемые языки обеспечивают блок finally, несмотря на то, что ресурсы все равно автоматически удаляются сборщиком мусора?
Подсказка: есть нечто большее, что вы можете сделать с «окончанием», чем просто освобождение памяти.
источник
new
не возвращает NULL, вместо этогоstd::shared_ptr
иstd::unique_ptr
непосредственно в stdlib.FWIW, Microsoft Visual C ++ поддерживает try, наконец, и он исторически использовался в приложениях MFC как метод отлова серьезных исключений, которые в противном случае могли бы привести к сбою. Например;
Я использовал это в прошлом, чтобы делать такие вещи, как сохранение резервных копий открытых файлов перед выходом. Некоторые параметры отладки JIT нарушают этот механизм.
источник
Как указано в других ответах, C ++ может поддерживать
finally
подобную функциональность. Реализация этой функциональности, которая, вероятно, наиболее близка к тому, чтобы быть частью стандартного языка, - это та, которая сопровождает основные руководящие принципы C ++ , набор лучших практик по использованию C ++, отредактированный Бьярном Стоуструпом и Хербом Саттером. Реализацияfinally
является частью библиотеки Guidelines поддержки (GSL). Повсюду в Руководстве рекомендуется использовать егоfinally
при работе с интерфейсами старого стиля, и у него также есть собственное руководство под названием Использовать объект final_action для выражения очистки, если нет подходящего дескриптора ресурса .Таким образом, C ++ не только поддерживает
finally
, но и рекомендуется использовать его во многих типичных случаях.Пример использования реализации GSL будет выглядеть так:
Внедрение и использование GSL очень похоже на ответ в ответе Паоло Больцони . Единственное отличие состоит в том, что объект, созданный с помощью,
gsl::finally()
не имеетdisable()
вызова. Если вам нужна эта функциональность (скажем, для возврата ресурса после того, как он собран, и исключений не должно быть), вы можете предпочесть реализацию Paolo. В противном случае использование GSL настолько близко к использованию стандартизированных функций, насколько это возможно.источник
Не совсем, но вы можете эмулировать их в некоторой степени, например:
Обратите внимание, что блок finally может сам выдать исключение до того, как исходное исключение будет переброшено, тем самым отбрасывая исходное исключение. Это то же самое поведение, что и в Java-блоке finally. Кроме того, вы не можете использовать
return
внутри блоков try & catch.источник
std::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e);
finally
блока.Я придумал
finally
макрос , который может быть использован почти как ¹ поfinally
ключевым словам в Java; он используетstd::exception_ptr
и друзей, лямбда-функции иstd::promise
, следовательно, требуетC++11
или выше; он также использует составное выражение GCC, которое также поддерживается clang.ВНИМАНИЕ : в более ранней версии этого ответа использовалась другая реализация концепции с гораздо большим количеством ограничений.
Сначала давайте определим вспомогательный класс.
Тогда есть фактический макрос.
Это можно использовать так:
Использование
std::promise
делает его очень простым для реализации, но, вероятно, также вносит немало ненужных накладных расходов, которых можно избежать, реализуя только необходимые функцииstd::promise
.AV CAVEAT: есть несколько вещей, которые не работают так же, как в java-версии
finally
. С верхней части моей головы:break
оператора из блоковtry
andcatch()
, поскольку они живут в лямбда-функции;catch()
блокtry
: это требование C ++;try
and нет возвратаcatch()'s
, компиляция завершится неудачно, посколькуfinally
макрос расширится до кода, который захочет вернуть avoid
. Это может быть, эээ, аннулируется ред при наличииfinally_noreturn
макрокоманды сортов.В общем, я не знаю, буду ли я когда-либо использовать этот материал сам, но было весело играть с ним. :)
источник
catch(xxx) {}
блок в началеfinally
макроса, где xxx - фиктивный тип только для того, чтобы иметь хотя бы один блок catch.catch(...)
, не так ли?xxx
в частном пространстве имен, которое никогда не будет использоваться.У меня есть сценарий использования, где я считаю, что он
finally
должен быть вполне приемлемой частью языка C ++ 11, так как я считаю, что его легче читать с точки зрения потока. Мой пример использования - цепочка потоков «потребитель / производитель», гдеnullptr
в конце цикла посылается дозорный, чтобы закрыть все потоки.Если бы C ++ это поддерживал, вы бы хотели, чтобы ваш код выглядел так:
Я думаю, что это более логично, чем помещать ваше объявление finally в начале цикла, так как это происходит после выхода из цикла ... но это заблуждение, потому что мы не можем сделать это в C ++. Обратите внимание, что очередь
downstream
подключена к другому потоку, поэтому вы не можете поместить дозорныйpush(nullptr)
в деструктор,downstream
потому что он не может быть уничтожен на этом этапе ... он должен оставаться в живых, пока другой поток не получитnullptr
.Итак, вот как использовать класс RAII с лямбдой, чтобы сделать то же самое:
и вот как вы это используете:
источник
Как утверждают многие, решение состоит в том, чтобы использовать функции C ++ 11, чтобы избежать блоков finally. Одной из особенностей является
unique_ptr
.Вот ответ Мефана, написанный с использованием шаблонов RAII.
Еще одно введение в использование unique_ptr с контейнерами стандартной библиотеки C ++ здесь
источник
Я хотел бы предоставить альтернативу.
Если вы хотите, чтобы блок finally вызывался всегда, просто поместите его после последнего блока catch (что, вероятно, должно быть
catch( ... )
для перехвата неизвестного исключения)Если вы хотите, чтобы блок finally был последним, что было сделано при возникновении какого-либо исключения, вы можете использовать логическую локальную переменную - перед запуском установите значение false и присвойте значение true в самом конце блока try, затем после проверки блока catch для переменной стоимость:
источник
Я также думаю, что RIIA не является полностью полезной заменой для обработки исключений и наличия наконец. Кстати, я также думаю, что RIIA это плохое имя во всем мире. Я называю эти типы классов «уборщиками» и использую их МНОГО. В 95% случаев они не инициализируют и не получают ресурсы, они применяют некоторые изменения на определенной основе или принимают что-то уже настроенное и проверяют, уничтожено ли оно. Это официальное название одержимого интернетом, которое я оскорбляю, даже если предположить, что мое имя может быть лучше.
Я просто не думаю, что разумно требовать, чтобы в каждой сложной настройке какого-то специального списка вещей был написан класс, содержащий его, чтобы избежать сложностей при очистке всего этого в случае необходимости перехватывать несколько типы исключений, если что-то идет не так в процессе. Это привело бы ко многим специальным классам, которые просто не были бы необходимы иначе.
Да, это хорошо для классов, которые предназначены для управления конкретным ресурсом, или для общих классов, которые предназначены для обработки набора похожих ресурсов. Но, даже если все вещи имеют такие оболочки, координация очистки может быть не просто вызовом деструкторов в обратном порядке.
Я думаю, что для C ++ имеет смысл иметь наконец-то. Я имею в виду, господи, за последние десятилетия на него было наклеено так много битов и бобов, что кажется, что странные люди внезапно станут консервативными из-за чего-то вроде, наконец, что может быть весьма полезным и, вероятно, не таким сложным, как некоторые другие вещи, которые были добавил (хотя это только предположение с моей стороны.)
источник
источник
finally
не делает.