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

Почему unique_ptr не освобождается после того, как конструктор вызывает исключение?

В следующем коде:

#include <memory>
#include <iostream>

void mydeallocator(int * x) {
    std::cerr << "Freeing memory" << std::endl;
    delete x;
}

struct Foo {
    std::unique_ptr <int,std::function <void(int*)>> x;
    Foo(bool fail) : x(new int(1),mydeallocator) {
        if(fail)
            throw std::runtime_error("We fail here");
    }
};

int main() {
    {auto foo1 = Foo(false);}
    {auto foo2 = Foo(true);}
}

Похоже, что память не освобождается должным образом при вызове Foo(true). А именно, когда мы компилируем и запускаем эту программу, мы получаем результат:

Freeing memory
terminate called after throwing an instance of 'std::runtime_error'
  what():  We fail here
Aborted

Я считаю, что сообщение Freeing memory следует вызывать дважды. В основном, в соответствии с этим question и людьми ISO С++ здесь и здесь, я понимаю, что стек должен раскрутиться над конструктором для Foo и что x должен вызвать его деструктор, который должен вызывать mydeallocator, Конечно, этого не происходит, так почему память не освобождается?

4b9b3361

Ответ 1

Исходный код throw;, когда вам нечего реконструировать. Это вызывает вызов std::terminate; стек не разматывается (и, следовательно, деструкторы не запускаются).

Ваш новый код генерирует исключение без его обработки. В этом случае, если стек разматывается, он определяется реализацией, поэтому он все еще отлично соответствует terminate() сразу. [except.terminate], акцент мой:

В некоторых ситуациях обработка исключений должна быть отменена за меньшую тонкие методы обработки ошибок. [Примечание: Эти ситуации:

  • когда механизм обработки исключений после завершения инициализации объекта исключения, но до активации обработчик для исключения (15.1), вызывает функцию, которая выходит через исключение или
  • , когда механизм обработки исключений не может найти обработчик для созданного исключения (15.3) или
  • когда поиск обработчика (15.3) встречает внешний блок функции с noexcept-спецификацией, которая не позволяет исключение (15.4) или
  • когда уничтожение объекта во время разматывания стека (15.2) завершается путем исключения исключения или
  • когда инициализация нелокальной переменной со статической или длительностью хранения потоков (3.6.2) завершается через исключение или
  • когда уничтожение объекта со статикой или продолжительностью хранения потоков завершается с помощью исключения (3.6.3) или
  • когда выполнение функции, зарегистрированной в std::atexit или std::at_quick_exit, завершается с помощью исключения (18.5) или
  • , когда выражение throw (5.17) без операнда пытается перестроить исключение и исключение не обрабатывается (15.1), или
  • когда std::unexpected завершается за исключением типа, который не допускается ранее описанной спецификацией исключения, и std:: bad_exception не входит в эту спецификацию исключения (15.5.2) или
  • когда вызывается вызванный по умолчанию обработчик исключений по умолчанию (D.8.1) или
  • когда вызывается функция std::nested_exception::rethrow_nested для объекта, который не исключил исключение (18.8.6), или
  • когда выполнение начальной функции потока завершается через исключение (30.3.1.2) или
  • когда деструктор или оператор назначения копирования вызывается на объект типа std::thread, который ссылается на соединяемую нить (30.3.1.3, 30.3.1.4) или
  • когда вызов функции wait(), wait_until() или wait_for() в переменной условия (30.5.1, 30.5.2) не отвечает Постусловие. -end note]

В таких случаях std::terminate() называется (18.8.3). В ситуации где встречный обработчик не найден, он определяется реализацией независимо от того, разорван ли стек до того, как std::terminate(). В ситуации, когда поиск обработчика (15.3) встречает внешний блок функции с помощью noexcept-specification, которая не допускает исключение (15.4), определяется реализацией, будет ли стека разматываться, разматываться частично или вообще не разматываться до вызова std::terminate(). Во всех других ситуациях стек не должен разматываться до std::terminate(). Реализация не допускается преждевременно прекращается, основываясь на процесс размотки вызовет вызов std::terminate().