Как освободить память в блоках try-catch?

у меня есть простой вопрос, надеюсь, - как одна свободная память, которая была выделена в блоке try при возникновении исключения? Рассмотрим следующий код:

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? Есть ли правило не выделять память на кучу в этих попытках .. catch блоки?

9 ответов


исследование RAII идиома (Приобретение Ресурсов Инициализация)! См., например,статья Википедии о RAII.

RAII - это просто общая идея. Он используется, например, в стандартной библиотеке C++std::unique_ptr или std::shared_ptr классы шаблон.


очень краткое объяснение идиомы RAII:

в основном, это язык C++ версии try..finally блоки найдено на некоторых других языках. Идиома 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`!
}

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?
}

либо переместить new до try, Так что указатель все еще находится в области или использовать смарт-указатель, как shared_ptr или unique_ptr (в крайнем случае, auto_ptr, но у него есть проблемы), которые будут убирать за вас на выходе. Исключения являются огромной причиной, почему умные указатели важны.


общий ответ-использовать 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 и shared_ptr, как упоминалось выше, но просто для полноты: в вашем примере вы можете заменить

char *heap = new char [50];

С

char *stack = static_cast<char*>( alloca(50) );

alloca практически идентичен malloc, за исключением того, что он выделяет память в стеке вместо кучи, поэтому независимо от того, как вы выходите из функции (бросая или сейчас), память будет восстановлена, и никакие удаления или освобождения не нужны.


Я должен согласиться со всеми теми, кто сказал RAII, однако, я бы использовал Boost's shared_array вместо auto_ptr. Автоматический указатель вызывает delete и не " удалить []", что приведет к утечкам с массивом.


самый простой способ-объявить переменную перед блоком try, а затем просто выполнить инициализацию внутри блока.


да-если вы рассматриваете простоту-указатель, который является внешним для вашего блока try, является решением.

в отношении


согласился с ответами на RAII и смарт-указатели.

однако, если вы настаиваете, вы можете сделать это:

try { dangerous operations } 
catch { cleanup; throw; }