Поддерживает ли C ++ «наконец a > 'блоки?
Что такое идиома RAII?
В чем разница между идиомой RAII C ++ и оператором C # 'using' ?
Поддерживает ли C ++ «наконец a > 'блоки?
Что такое идиома RAII?
В чем разница между идиомой RAII C ++ и оператором C # 'using' ?
Нет, C ++ не поддерживает блоки finally. Причина в том, что C ++ вместо этого поддерживает RAII: «Получение ресурсов - это инициализация» - плохое название † для действительно полезной концепции.
Идея состоит в том, что деструктор объекта отвечает за освобождение ресурсов. Когда у объекта есть автоматическая продолжительность хранения, деструктор объекта будет вызываться при выходе из блока, в котором он был создан, даже если этот блок завершается при наличии исключения. Вот объяснение Бьярна Страуструпа по этой теме.
Обычно RAII используется для блокировки мьютекса:
// A class with implements RAII
class lock
{
mutex &m_;
public:
lock(mutex &m)
: m_(m)
{
m.acquire();
}
~lock()
{
m_.release();
}
};
// A class which uses 'mutex' and 'lock' objects
class foo
{
mutex mutex_; // mutex for locking 'foo' object
public:
void bar()
{
lock scopeLock(mutex_); // lock object.
foobar(); // an operation which may throw an exception
// scopeLock will be destructed even if an exception
// occurs, which will release the mutex and allow
// other functions to lock the object and run.
}
};
RAII также упрощает использование объектов как членов других классов. Когда класс-владелец уничтожается, ресурс, управляемый классом RAII, освобождается, потому что в результате вызывается деструктор для класса, управляемого RAII. Это означает, что когда вы используете RAII для всех членов в классе, которые управляют ресурсами, вы можете обойтись без использования очень простого, возможно, даже стандартного деструктора для класса-владельца, поскольку ему не нужно вручную управлять временем жизни его членов. . (Спасибо Майку Б. за указание на это.)
Для тех, кто знаком с C # или VB.NET, вы можете узнать, что RAII похож на Детерминированное уничтожение .NET с помощью операторов IDisposable и 'using'. Действительно, эти два метода очень похожи. Основное отличие состоит в том, что RAII детерминированно высвобождает ресурсы любого типа, включая память. При реализации IDisposable в .NET (даже в языке .NET C ++ / CLI) ресурсы будут выделены детерминированно, за исключением памяти. В .NET память не выделяется детерминированно; память освобождается только во время циклов сборки мусора.
† Некоторые люди считают, что «Разрушение - это отказ от ресурсов» - более точное название идиомы RAII.
В C ++ оператор finally НЕ требуется из-за RAII.
RAII переносит ответственность за безопасность исключений с пользователя объекта на проектировщика (и разработчика) объекта. Я бы сказал, что это правильное место, поскольку тогда вам нужно только один раз получить правильную безопасность исключений (в дизайне / реализации). Используя finally, вам нужно обеспечить правильную безопасность исключений каждый раз, когда вы используете объект.
Также IMO код выглядит аккуратнее (см. Ниже).
Пример:
Объект базы данных. Чтобы убедиться, что соединение с БД используется, оно должно быть открыто и закрыто. Используя RAII, это можно сделать в конструкторе / деструкторе.
Использование RAII значительно упрощает правильное использование объекта БД. Объект DB будет правильно закрываться с помощью деструктора, как бы мы ни пытались злоупотребить им.
void someFunc()
{
DB db("DBDesciptionString");
// Use the db object.
} // db goes out of scope and destructor closes the connection.
// This happens even in the presence of exceptions.
При использовании, наконец, правильное использование объекта делегируется пользователю объекта. т.е. Ответственность за правильное явное закрытие соединения с БД лежит на пользователе объекта. Теперь вы можете утверждать, что это можно сделать в финализаторе, но ресурсы могут иметь ограниченную доступность или другие ограничения, и поэтому вы обычно хотите контролировать выпуск объекта, а не полагаться на недетерминированное поведение сборщика мусора.
void someFunc()
{
DB db = new DB("DBDesciptionString");
try
{
// Use the db object.
}
finally
{
// Can not rely on finaliser.
// So we must explicitly close the connection.
try
{
db.close();
}
catch(Throwable e)
{
/* Ignore */
// Make sure not to throw exception if one is already propagating.
}
}
}
Также это простой пример.
Когда у вас есть несколько ресурсов, которые необходимо освободить, код может стать сложным.
Более подробный анализ можно найти здесь: http://accu.org/index.php/journals/236
C ++ как RAII
try
: переход к ближайшему к вам стандарту (C ++ gsl = ›modernescpp.com/index.php/)
- person PieterNuyts; 02.03.2020
Кроме того, Основные рекомендации по C ++ наконец дали свои результаты.
Вот ссылка на реализацию GSL Microsoft и ссылку на Реализация Мартина Моэна
Бьярн Страуструп несколько раз говорил, что все, что есть в GSL, в конечном итоге должно войти в стандарт. Так что это должен быть перспективный способ использования finally.
Вы можете легко реализовать себя, если хотите, продолжайте читать.
В C ++ 11 RAII и лямбды позволяют окончательно сделать общее:
пример использования:
namespace detail { //adapt to your "private" namespace
template <typename F>
struct FinalAction {
FinalAction(F f) : clean_{f} {}
~FinalAction() { if(enabled_) clean_(); }
void disable() { enabled_ = false; };
private:
F clean_;
bool enabled_{true}; }; }
template <typename F>
detail::FinalAction<F> finally(F f) {
return detail::FinalAction<F>(f); }
вывод будет:
#include <iostream>
int main() {
int* a = new int;
auto delete_a = finally([a] { delete a; std::cout << "leaving the block, deleting a!\n"; });
std::cout << "doing something ...\n"; }
Лично я использовал это несколько раз, чтобы закрыть дескриптор файла POSIX в программе на C ++.
doing something...
leaving the block, deleting a!
Обычно лучше иметь настоящий класс, который управляет ресурсами и, таким образом, избегает любых утечек, но это наконец полезно в тех случаях, когда создание класса звучит как излишество.
Кроме того, мне он нравится больше, чем другие языки наконец, потому что при естественном использовании вы пишете код закрытия рядом с кодом открытия (в моем примере это новый и удалить strong>) и уничтожение следует за построением в порядке LIFO, как обычно в C ++. Единственным недостатком является то, что вы получаете автоматическую переменную, которую на самом деле не используете, а лямбда-синтаксис делает ее немного шумной (в моем примере в четвертой строке только слово finally и {} -block справа значимы, остальное по сути шум).
Другой пример:
Элемент disable полезен, если finally нужно вызывать только в случае сбоя. Например, вам нужно скопировать объект в три разных контейнера, вы можете настроить наконец для отмены каждой копии и отключения после того, как все копии будут успешными. Поступая так, если разрушение не может бросить, вы гарантируете сильную гарантию.
[...]
auto precision = std::cout.precision();
auto set_precision_back = finally( [precision, &std::cout]() { std::cout << std::setprecision(precision); } );
std::cout << std::setprecision(3);
Пример отключения:
Если вы не можете использовать C ++ 11, вы все равно можете иметь finally, но код становится немного длиннее. Просто определите структуру только с конструктором и деструктором, конструктор будет ссылаться на все необходимое, а деструктор выполнит необходимые действия. Это в основном то, что делает лямбда, выполняемая вручную.
//strong guarantee
void copy_to_all(BIGobj const& a) {
first_.push_back(a);
auto undo_first_push = finally([first_&] { first_.pop_back(); });
second_.push_back(a);
auto undo_second_push = finally([second_&] { second_.pop_back(); });
third_.push_back(a);
//no necessary, put just to make easier to add containers in the future
auto undo_third_push = finally([third_&] { third_.pop_back(); });
undo_first_push.disable();
undo_second_push.disable();
undo_third_push.disable(); }
Надеюсь, вы сможете использовать C ++ 11, этот код больше показывает, что C ++ не поддерживает, наконец, это ерунда с самых первых недель C ++, такой код можно было писать еще до того, как C ++ получил свое имя.
#include <iostream>
int main() {
int* a = new int;
struct Delete_a_t {
Delete_a_t(int* p) : p_(p) {}
~Delete_a_t() { delete p_; std::cout << "leaving the block, deleting a!\n"; }
int* p_;
} delete_a(a);
std::cout << "doing something ...\n"; }
Помимо упрощения очистки с помощью объектов на основе стека, RAII также полезен, потому что такая же «автоматическая» очистка происходит, когда объект является членом другого класса. Когда класс-владелец разрушается, ресурс, управляемый классом RAII, очищается, потому что в результате вызывается dtor для этого класса.
Это означает, что когда вы достигаете нирваны RAII и все члены класса используют RAII (например, интеллектуальные указатели), вы можете обойтись очень простым (возможно, даже по умолчанию) dtor для класса-владельца, поскольку ему не нужно вручную управлять своим время жизни членского ресурса.
почему даже управляемые языки предоставляют finally-блок, несмотря на то, что ресурсы в любом случае автоматически освобождаются сборщиком мусора?
Собственно, языкам, основанным на сборщиках мусора, нужно «наконец-то» больше. Сборщик мусора не уничтожает ваши объекты своевременно, поэтому на него нельзя положиться для правильной очистки проблем, не связанных с памятью.
Что касается динамически распределяемых данных, многие утверждают, что вам следует использовать интеллектуальные указатели.
Тем не мение...
RAII переносит ответственность за безопасность исключений с пользователя объекта на дизайнера.
К сожалению, это его собственная ошибка. От старых привычек программирования на C трудно избавиться. Когда вы используете библиотеку, написанную на C или очень в стиле C, RAII использоваться не будет. Если не считать переписывания всего интерфейса API, это как раз то, с чем вам придется работать. Тогда отсутствие «наконец-то» действительно кусает.
Еще одна эмуляция блока "finally" с использованием лямбда-функций C ++ 11
Dispose
, но такой подход требует, чтобы класс, реализующий его, знал о том, как грязный флаг, и т. д. (что может быть сложно, если несколько приложений могут получить доступ к разным частям файла одновременно).
- person supercat; 03.12.2012
Close
, который имеет прямое отношение к сборщику мусора. Думал не на том языке ... см. Перезапись:
- person supercat; 03.12.2012
finalize
; эффективно пытается вывести сборщик мусора из строя. Хороший выбор здесь - растопить. В java / .net я считаю, что лучше иметь отдельную очистку при ошибке для очистки при успехе, когда я откатываюсь от фиксации и игнорирую (только журнал) против исключения исключения. В результате я теперь наконец все реже и реже пользуюсь. Я считаю, что разработчик ни в коем случае не должен позволять этому вложенному исключению влиять на выполнение программы.
- person Philip Couling; 04.12.2012
finalize()
. Из этих различий наиболее частым является обработка исключений.
- person Philip Couling; 04.12.2012
if
и хочет знать, (1) успешно ли он, (2) не удалось без побочных эффектов, (3) не удалось с побочными эффектами эффекты, с которыми вызывающий абонент готов справиться, или (4) потерпели неудачу с побочными эффектами, с которыми вызывающий абонент не может справиться. Только вызывающий будет знать, с какими ситуациями он может и не может справиться; звонящему нужен способ узнать, в какой ситуации. Жаль, что нет стандартного механизма для предоставления наиболее важной информации об исключении.
- person Philip Couling; 04.12.2012
SomeObject.DoSomething()
, который инициализирует дескриптор в своем конструкторе, освобождает его в своем деструкторе и имеет класс _2_, чтобы вы могли использовать API. Возможно, это не самый лучший вариант, но это RAII для вашей библиотеки C, и он займет максимум ~ 20 строк (это может вызвать дополнительные изменения в вызывающем коде, и если вы считаете, что это слишком многословно, может быть уместно разыменование перегрузки)
- person supercat; 04.12.2012
Будем надеяться, что компилятор оптимизирует приведенный выше код.
template <typename TCode, typename TFinallyCode>
inline void with_finally(const TCode &code, const TFinallyCode &finally_code)
{
try
{
code();
}
catch (...)
{
try
{
finally_code();
}
catch (...) // Maybe stupid check that finally_code mustn't throw.
{
std::terminate();
}
throw;
}
finally_code();
}
Теперь мы можем написать такой код:
При желании вы можете заключить эту идиому в макрос «попробуйте - наконец»:
with_finally(
[&]()
{
try
{
// Doing some stuff that may throw an exception
}
catch (const exception1 &)
{
// Handling first class of exceptions
}
catch (const exception2 &)
{
// Handling another class of exceptions
}
// Some classes of exceptions can be still unhandled
},
[&]() // finally
{
// This code will be executed in all three cases:
// 1) exception was not thrown at all
// 2) exception was handled by one of the "catch" blocks above
// 3) exception was not handled by any of the "catch" block above
}
);
Теперь в C ++ 11 доступен блок «finally»:
// Please never throw exception below. It is needed to avoid a compilation error
// in the case when we use "begin_try ... finally" without any "catch" block.
class never_thrown_exception {};
#define begin_try with_finally([&](){ try
#define finally catch(never_thrown_exception){throw;} },[&]()
#define end_try ) // sorry for "pascalish" style :(
Лично мне не нравится «макро» версия идиомы «finally», и я бы предпочел использовать чистую функцию «with_finally», хотя синтаксис в этом случае более громоздкий.
begin_try
{
// A code that may throw
}
catch (const some_exception &)
{
// Handling some exceptions
}
finally
{
// A code that is always executed
}
end_try; // Sorry again for this ugly thing
Вы можете протестировать приведенный выше код здесь: http://coliru.stacked-crooked.com/a/1d88f64cb27b3813
PS
Если вам нужен блок finally в коде, тогда охрана с ограниченным объемом или ON_FINALLY/ON_EXCEPTION, вероятно, лучше подойдут для ваших нужд.
Вот краткий пример использования ON_FINALLY / ON_EXCEPTION:
Извините, что откопал такую старую ветку, но есть серьезная ошибка в следующих рассуждениях:
void function(std::vector<const char*> &vector)
{
int *arr1 = (int*)malloc(800*sizeof(int));
if (!arr1) { throw "cannot malloc arr1"; }
ON_FINALLY({ free(arr1); });
int *arr2 = (int*)malloc(900*sizeof(int));
if (!arr2) { throw "cannot malloc arr2"; }
ON_FINALLY({ free(arr2); });
vector.push_back("good");
ON_EXCEPTION({ vector.pop_back(); });
...
RAII переносит ответственность за безопасность исключений с пользователя объекта на проектировщика (и разработчика) объекта. Я бы сказал, что это правильное место, поскольку тогда вам нужно только один раз получить правильную безопасность исключений (в дизайне / реализации). Используя finally, вам нужно обеспечить правильную безопасность исключений каждый раз, когда вы используете объект.
Чаще всего вам приходится иметь дело с динамически выделяемыми объектами, динамическим числом объектов и т. Д. В блоке try некоторый код может создать много объектов (сколько определяется во время выполнения) и сохранить указатели на них в списке. Это не экзотический сценарий, а очень распространенный. В этом случае вам нужно написать что-нибудь вроде
Конечно, сам список будет уничтожен при выходе из области видимости, но это не очистит созданные вами временные объекты.
void DoStuff(vector<string> input)
{
list<Foo*> myList;
try
{
for (int i = 0; i < input.size(); ++i)
{
Foo* tmp = new Foo(input[i]);
if (!tmp)
throw;
myList.push_back(tmp);
}
DoSomeStuff(myList);
}
finally
{
while (!myList.empty())
{
delete myList.back();
myList.pop_back();
}
}
}
Вместо этого вы должны пойти по уродливому маршруту:
Также: почему даже управляемые языки предоставляют finally-блок, несмотря на то, что ресурсы в любом случае автоматически освобождаются сборщиком мусора?
void DoStuff(vector<string> input)
{
list<Foo*> myList;
try
{
for (int i = 0; i < input.size(); ++i)
{
Foo* tmp = new Foo(input[i]);
if (!tmp)
throw;
myList.push_back(tmp);
}
DoSomeStuff(myList);
}
catch(...)
{
}
while (!myList.empty())
{
delete myList.back();
myList.pop_back();
}
}
Подсказка: с помощью «finally» вы можете сделать больше, чем просто освобождение памяти.
FWIW, Microsoft Visual C ++, наконец, поддерживает try, и исторически он использовался в приложениях MFC как метод перехвата серьезных исключений, которые в противном случае привели бы к сбою. Например;
Я использовал это в прошлом, чтобы делать такие вещи, как сохранение резервных копий открытых файлов перед выходом. Однако некоторые настройки отладки JIT нарушают этот механизм.
int CMyApp::Run()
{
__try
{
int i = CWinApp::Run();
m_Exitok = MAGIC_EXIT_NO;
return i;
}
__finally
{
if (m_Exitok != MAGIC_EXIT_NO)
FaultHandler();
}
}
Как указано в других ответах, C ++ может поддерживать
int CMyApp::Run()
{
__try
{
int i = CWinApp::Run();
m_Exitok = MAGIC_EXIT_NO;
return i;
}
__finally
{
if (m_Exitok != MAGIC_EXIT_NO)
FaultHandler();
}
}
-подобные функции. Реализация этой функции, которая, вероятно, наиболее близка к тому, чтобы быть частью стандартного языка, - это реализация, сопровождающая Основные принципы C ++, набор рекомендаций по использованию C ++ под редакцией Бьярна Стауструпа и Херба Саттера. реализация _2_ является частью Библиотека поддержки рекомендаций (GSL). В Руководстве рекомендуется использовать _3_ при работе с интерфейсами в старом стиле, а также для него есть собственное руководство под названием Используйте объект final_action, чтобы выразить очистку, если не подходит доступен дескриптор ресурса.
Таким образом, C ++ не только поддерживает finally
, но и рекомендуется использовать его во многих распространенных сценариях использования.
Пример использования реализации GSL будет выглядеть так:
Реализация и использование GSL очень похожи на те, что описаны в ответе Паоло Болзони. Одно отличие состоит в том, что объект, созданный gsl::finally()
, не имеет вызова disable()
. Если вам нужна эта функциональность (скажем, чтобы вернуть ресурс после его сборки и никаких исключений не должно произойти), вы можете предпочесть реализацию Паоло. В противном случае использование GSL максимально приближено к использованию стандартных функций.
#include <gsl/gsl_util.h>
void example()
{
int handle = get_some_resource();
auto handle_clean = gsl::finally([&handle] { clean_that_resource(handle); });
// Do a lot of stuff, return early and throw exceptions.
// clean_that_resource will always get called.
}
У меня есть случай использования, когда я думаю, что finally
должен быть вполне приемлемой частью языка C ++ 11, поскольку я думаю, что его легче читать с точки зрения потока. Мой вариант использования - это цепочка потоков потребитель / производитель, где в конце выполнения отправляется дозорный finally
, чтобы закрыть все потоки.
Если бы C ++ поддерживал это, вы бы хотели, чтобы ваш код выглядел так:
Я думаю, что это более логично, чем помещать ваше объявление finally в начале цикла, поскольку оно происходит после выхода из цикла ... но это принятие желаемого за действительное, потому что мы не можем сделать это на C ++. Обратите внимание на то, что очередь downstream
подключена к другому потоку, поэтому вы не можете поместить дозор push(nullptr)
в деструктор downstream
, потому что он не может быть уничтожен в этот момент ... он должен оставаться в живых, пока другой поток не получит nullptr
.
extern Queue downstream, upstream;
int Example()
{
try
{
while(!ExitRequested())
{
X* x = upstream.pop();
if (!x) break;
x->doSomething();
downstream.push(x);
}
}
finally {
downstream.push(nullptr);
}
}
Итак, вот как использовать класс RAII с лямбдой, чтобы сделать то же самое:
и вот как вы его используете:
class Finally
{
public:
Finally(std::function<void(void)> callback) : callback_(callback)
{
}
~Finally()
{
callback_();
}
std::function<void(void)> callback_;
};
Не совсем, но вы можете в некоторой степени имитировать, например:
extern Queue downstream, upstream;
int Example()
{
Finally atEnd([](){
downstream.push(nullptr);
});
while(!ExitRequested())
{
X* x = upstream.pop();
if (!x) break;
x->doSomething();
downstream.push(x);
}
}
Обратите внимание, что блок finally может сам вызвать исключение до того, как исходное исключение будет создано повторно, тем самым отбросив исходное исключение. Это то же самое поведение, что и в конечном блоке Java. Кроме того, вы не можете использовать return
внутри блоков try & catch.
int * array = new int[10000000];
try {
// Some code that can throw exceptions
// ...
throw std::exception();
// ...
} catch (...) {
// The finally-block (if an exception is thrown)
delete[] array;
// re-throw the exception.
throw;
}
// The finally-block (if no exception was thrown)
delete[] array;
Я придумал макрос
int * array = new int[10000000];
try {
// Some code that can throw exceptions
// ...
throw std::exception();
// ...
} catch (...) {
// The finally-block (if an exception is thrown)
delete[] array;
// re-throw the exception.
throw;
}
// The finally-block (if no exception was thrown)
delete[] array;
, который можно использовать почти как ¹ ключевое слово return
в Java; в нем используются _3_ и другие, лямбда-функции и _4_, поэтому требуется _5_ или выше; он также использует выражение составного оператора Расширение GCC, которое также поддерживается clang.
ПРЕДУПРЕЖДЕНИЕ: в более ранней версии этого ответа использовалась другая реализация концепции со многими другими ограничениями.
Сначала давайте определим вспомогательный класс.
Тогда есть собственно макрос.
#include <future>
template <typename Fun>
class FinallyHelper {
template <typename T> struct TypeWrapper {};
using Return = typename std::result_of<Fun()>::type;
public:
FinallyHelper(Fun body) {
try {
execute(TypeWrapper<Return>(), body);
}
catch(...) {
m_promise.set_exception(std::current_exception());
}
}
Return get() {
return m_promise.get_future().get();
}
private:
template <typename T>
void execute(T, Fun body) {
m_promise.set_value(body());
}
void execute(TypeWrapper<void>, Fun body) {
body();
}
std::promise<Return> m_promise;
};
template <typename Fun>
FinallyHelper<Fun> make_finally_helper(Fun body) {
return FinallyHelper<Fun>(body);
}
Его можно использовать так:
#define try_with_finally for(auto __finally_helper = make_finally_helper([&] { try
#define finally }); \
true; \
({return __finally_helper.get();})) \
/***/
Использование std::promise
делает его очень простым в реализации, но, вероятно, также вносит немало ненужных накладных расходов, которых можно было бы избежать, повторно реализовав только необходимые функции из std::promise
.
void test() {
try_with_finally {
raise_exception();
}
catch(const my_exception1&) {
/*...*/
}
catch(const my_exception2&) {
/*...*/
}
finally {
clean_it_all_up();
}
}
¹ ПРЕДОСТЕРЕЖЕНИЕ: есть несколько вещей, которые работают не так, как java-версия finally
. С верхней части моей головы:
В общем, я не знаю, буду ли я когда-нибудь использовать эту штуку сам, но с ней было весело играть. :)
try
должен быть хотя бы один catch()
блок: это требование C ++;try
и catch()'s
, компиляция завершится неудачно, потому что макрос finally
расширится до кода, который захочет вернуть void
. Это могло быть, ошибочно, недействительным из-за наличия макроса finally_noreturn
.finally
.невозможно выйти из внешнего цикла с помощью оператора break
из блоков try
и catch()
, поскольку они находятся внутри лямбда-функции;
catch(xxx) {}
, не так ли?
- person Mark Lakata; 02.08.2016
catch(...)
в частном пространстве имен, которое никогда не будет использоваться.
- person Fabio A.; 02.08.2016
xxx
. Clang делает то же самое. Я считаю, что стандарт просто запрещает иметь _2_ блок посередине.
- person Mark Lakata; 02.08.2016
Вот ответ Мефана, написанный с использованием шаблонов RAII.
Еще одно введение в использование unique_ptr с контейнерами стандартной библиотеки C ++: здесь
#include <vector>
#include <memory>
#include <list>
using namespace std;
class Foo
{
...
};
void DoStuff(vector<string> input)
{
list<unique_ptr<Foo> > myList;
for (int i = 0; i < input.size(); ++i)
{
myList.push_back(unique_ptr<Foo>(new Foo(input[i])));
}
DoSomeStuff(myList);
}
Я также считаю, что RIIA не является полностью полезной заменой обработки исключений и наличия файла finally. Кстати, я также считаю, что RIIA - плохая репутация. Я называю эти классы «дворниками» и использую их МНОГОЧИСЛЕННО. В 95% случаев они не инициализируют и не собирают ресурсы, они вносят какие-то изменения в определенную область действия или берут что-то уже настроенное и проверяют, что это уничтожено. Это официальное имя шаблона, одержимого Интернетом, меня ругают за то, что я даже предполагаю, что мое имя может быть лучше.
Я просто не думаю, что разумно требовать, чтобы при каждой сложной настройке какого-то специального списка вещей был написан класс, содержащий его, чтобы избежать осложнений при очистке всего этого резервного копирования перед лицом необходимости поймать несколько типы исключений, если что-то пойдет не так в процессе. Это привело бы к появлению множества специальных классов, которые в противном случае просто не понадобились бы.
Да, это нормально для классов, которые предназначены для управления конкретным ресурсом, или для общих классов, которые предназначены для обработки набора аналогичных ресурсов. Но даже если у всех задействованных вещей есть такие оболочки, координация очистки может быть не просто вызовом деструкторов в обратном порядке.
Я думаю, что для C ++ имеет смысл иметь наконец. Я имею в виду, черт возьми, за последние десятилетия на него было приклеено так много мелочей и бобов, что кажется, что странные люди внезапно станут консервативными по сравнению с чем-то вроде finally, которое может быть весьма полезным и, вероятно, не таким сложным, как некоторые другие вещи, которые были добавил (хотя это всего лишь предположение с моей стороны.)
ИЗМЕНЕНО
Если вы не прерываете / продолжаете / не возвращаетесь и т. д., вы можете просто добавить уловку к любому неизвестному исключению и поместить за ним код Always. Это также происходит, когда вам не нужно генерировать исключение повторно.
Обычно, наконец, на других языках программирования обычно выполняется независимо от того, что (обычно означает независимо от любого возврата, прерывания, продолжения, ...) за исключением какой-то системы exit()
, которая сильно отличается в зависимости от языка программирования - например, PHP и Java просто завершают работу в этот момент, но Python все равно выполняется, а затем завершается.
try{
// something that might throw exception
} catch( ... ){
// what to do with uknown exception
}
//final code to be called always,
//don't forget that it might throw some exception too
doSomeCleanUp();
Но код, который я описал выше, не работает таким образом
= ›следующий код выводит ТОЛЬКО something wrong!
:
Так в чем проблема?
#include <stdio.h>
#include <iostream>
#include <string>
std::string test() {
try{
// something that might throw exception
throw "exceptiooon!";
return "fine";
} catch( ... ){
return "something wrong!";
}
return "finally";
}
int main(void) {
std::cout << test();
return 0;
}