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

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

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

24
задан Brian Tompsett - 汤莱恩 6 July 2015 в 20:39
поделиться

9 ответов

Изучите идиому 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`!
}
34
ответ дан 28 November 2019 в 22:51
поделиться

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

6
ответ дан 28 November 2019 в 22:51
поделиться

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

0
ответ дан 28 November 2019 в 22:51
поделиться

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?
}
9
ответ дан 28 November 2019 в 22:51
поделиться

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

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

try { dangerous operations } 
catch { cleanup; throw; }
-4
ответ дан 28 November 2019 в 22:51
поделиться

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

char *heap = new char [50];

на

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

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

3
ответ дан 28 November 2019 в 22:51
поделиться

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

Мир

9
ответ дан 28 November 2019 в 22:51
поделиться

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

1
ответ дан 28 November 2019 в 22:51
поделиться

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

С уважением

0
ответ дан 28 November 2019 в 22:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: