Подтвердить что ты не робот

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

4b9b3361

Ответ 1

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

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


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

В принципе, это версия С++ версии 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`!
}

Ответ 2

ОК mister 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?
}

Ответ 3

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

Ответ 4

Общий ответ - использование 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.

Мир

Ответ 5

"Правильный" ответ - это RAII и shared_ptr, как упоминалось выше, но только для того, чтобы быть полным: в вашем примере вы можете заменить

char *heap = new char [50];

с

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

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

Привет

Ответ 9

Согласовано с ответами на RAII и интеллектуальными указателями.

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

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