У меня есть простой вопрос, надо надеяться - как делает одну свободную память, которая была выделена в блоке попытки, когда исключение происходит? Рассмотрите следующий код:
try
{
char *heap = new char [50];
//let exception occur here
delete[] heap;
}
catch (...)
{
cout << "Error, leaving function now";
//delete[] heap; doesn't work of course, heap is unknown to compiler
return 1;
}
Как может я свободная память после того, как "куча" была выделена, и исключение произошло перед вызовом delete[] heap
? Есть ли правило не выделить память на "куче" в них, пробуют.. блоки выгоды?
Изучите идиому RAII ( Получение ресурсов - это инициализация )! См., Например, статья в Википедии о RAII .
RAII - это всего лишь общая идея. Он используется, например, в классах шаблонов std :: unique_ptr
или std :: shared_ptr
стандартной библиотеки C ++.
Очень краткое объяснение идиомы RAII:
По сути, это C ++ версия блоков try..inally
, найденных в некоторых других языках. Идиома RAII, возможно, более гибкая.
Это работает следующим образом:
Вы пишете класс-оболочку вокруг своего ресурса (например, памяти). Деструктор отвечает за освобождение ресурса.
Вы создаете как локальную (автоматическую) переменную экземпляр вашего класса-оболочки в области видимости. Как только выполнение программы покидает эту область, вызывается деструктор объекта, освобождая тем самым ресурс (например, память).
Важным моментом является , что не имеет значения , как выходят из области видимости. Даже если генерируется исключение, область все еще закрывается, и деструктор объекта-оболочки по-прежнему вызывается.
Очень грубый пример:
// BEWARE: this is NOT a good implementation at all, but is supposed to
// give you a general idea of how RAII is supposed to work:
template <typename T>
class wrapper_around
{
public:
wrapper_around(T value)
: _value(value)
{ }
T operator *()
{
return _value;
}
virtual ~wrapper_around()
{
delete _value; // <-- NOTE: this is incorrect in this particular case;
// if T is an array type, delete[] ought to be used
}
private:
T _value;
};
// ...
{
wrapper_around<char*> heap( new char[50] );
// ... do something ...
// no matter how the { } scope in which heap is defined is exited,
// if heap has a destructor, it will get called when the scope is left.
// Therefore, delegate the responsibility of managing your allocated
// memory to the `wrapper_around` template class.
// there are already existing implementations that are much better
// than the above, e.g. `std::unique_ptr` and `std::shared_ptr`!
}
Либо переместите new
до try
, чтобы указатель все еще находился в области действия, либо используйте умный указатель, например shared_ptr
или unique_ptr
(в крайнем случае, auto_ptr
, но у него есть проблемы), который очистит вас при выходе. Исключения - огромная причина важности умных указателей.
Самый простой способ - объявить переменную перед блоком try, а затем просто выполнить инициализацию внутри блока.
OK, господин программист Java:
try
{
// Exception safe dynamic allocation of a block of memory.
std::vector<char> heap(50);
// DO STUFF
// Note in C++ we use stack based objects and their constructor/destructor
// TO give a deterministic cleanup, even in the presence of exceptions.
//
// Look up RAII (bad name for a fantastic concept).
}
catch (...)
{
cout << "Error, leaving function now";
return 1; // Though why you want to return when you have not fixed the exception is
// slightly strange. Did you want to rethrow?
}
Согласен с ответами по RAII и умным указателям.
Однако, если вы настаиваете, вы можете сделать следующее:
try { dangerous operations }
catch { cleanup; throw; }
«Правильный» ответ - RAII и shared_ptr, как упоминалось выше, но для полноты: в вашем примере вы можете заменить
char *heap = new char [50];
на
char *stack = static_cast<char*>( alloca(50) );
alloca
почти идентична в malloc
, за исключением того, что он выделяет память в стеке, а не в куче, поэтому независимо от того, как вы завершаете работу (выброс или сейчас), память будет освобождена, и никаких удалений или освобождений не требуется.
Общий ответ - использовать RAII.
Тем не менее, ее можно решить, переместив переменную из области действия try {}:
char * heap = NULL;
try {
heap = new char [50];
... stuff ...
} catch (...) {
if (heap) {
delete[] heap;
heap = NULL;
}
... however you want to handle the exception: rethrow, return, etc ...
}
Обратите внимание, что я не рекомендую это как хорошую практику - скорее, это просто бесполезно, чтобы использовать только если вы действительно знаете о рисках и по-прежнему готовы их принимать. Лично я бы использовал RAII.
Мир
Я должен согласиться со всеми, кто сказал RAII, однако я бы использовал Boost's shared_array вместо auto_ptr. Автоматический указатель вызывает delete
, а не 'delete []', что приведет к утечкам с массивом.
Да - если вы рассматриваете простоту - указатель, который является внешним по отношению к вашему блоку try, является решением.
С уважением