Как исключения выделяются на стеке, пойманном вне их объема?

В следующем коде стековая переменная 'исключая' брошена и поймана в функции вне объема, в котором исключая был объявлен. Это кажется немного странным для меня, начиная с (AFAIK) стековые переменные не могут использоваться вне объема, в котором они были объявлены (стек раскручен).

void f() {
    SomeKindOfException ex(...);
    throw ex;
}

void g() {
    try {
        f();
    } catch (SomeKindOfException& ex) {
        //Handling code...
    }
}

Я добавил оператор печати к деструктору SomeKindOfException, и он показывает, что исключая разрушен, как только он выходит из объема в f (), но тогда он пойман в g () и разрушен снова, как только он выходит из объема там также.

Какая-либо справка?

23
задан Paul Nathan 8 March 2010 в 20:03
поделиться

6 ответов

Объект исключения копируется в специальное место, чтобы выжить при раскручивании стека. Причина, по которой вы видите два разрушения, заключается в том, что при выходе из f () исходное исключение уничтожается, а при выходе из g () уничтожается копия.

20
ответ дан 29 November 2019 в 01:32
поделиться

Стандарт C ++ 15.1 / 4:

Память для временной копии создаваемого исключения выделяется неопределенным образом, за исключением случаев, указанных в п. 3.7.3.1. Временное состояние сохраняется до тех пор, пока для этого исключения выполняется обработчик. В частности, если обработчик завершает работу, выполнив бросок; оператор, который передает управление другому обработчику для того же исключения, поэтому временное значение остается. Когда последний обработчик, выполняемый для исключения , завершается любым способом, кроме throw; временный объект уничтожается, и реализация может освободить память для временного объекта; любое такое освобождение выполняется неопределенным образом. Уничтожение происходит сразу после уничтожения объекта, объявленного в объявлении исключения в обработчике.

Больше сказать нечего.

10
ответ дан 29 November 2019 в 01:32
поделиться

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

Ссылка ex затем привязивается к тому объекту исключения, который является временным (у него нет имени).

10
ответ дан 29 November 2019 в 01:32
поделиться

Поскольку в спецификации явно указано, что временный объект создается вместо операнда throw .

1
ответ дан 29 November 2019 в 01:32
поделиться

Когда вы выбрасываете ex, он копируется в специальную ячейку памяти, используемую для созданных объектов исключения. Такое копирование выполняется обычным конструктором копирования.

Вы можете легко увидеть это из этого примера:

#include <iostream>

void ThrowIt();

class TestException
{
  public:
    TestException()
    {
        std::cerr<<this<<" - inside default constructor"<<std::endl;
    }

    TestException(const TestException & Right)
    {
        (void)Right;
        std::cerr<<this<<" - inside copy constructor"<<std::endl;
    }

    ~TestException()
    {
        std::cerr<<this<<" - inside destructor"<<std::endl;    
    }
};

int main()
{
    try
    {
        ThrowIt();
    }
    catch(TestException & ex)
    {
        std::cout<<"Caught exception ("<<&ex<<")"<<std::endl;
    }
    return 0;
}

void ThrowIt()
{
    TestException ex;
    throw ex;
}

Пример вывода:

matteo@teolapubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic ExceptionStack.cpp -o ExceptionStack.x
matteo@teolapubuntu:~/cpp/test$ ./ExceptionStack.x 
0xbf8e202f - inside default constructor
0x9ec0068 - inside copy constructor
0xbf8e202f - inside destructor
Caught exception (0x9ec0068)
0x9ec0068 - inside destructor

Кстати, вы можете видеть здесь, что место в памяти, используемое для брошенного объекта (0x09ec0068), определенно далеко от одного из исходный объект (0xbf8e202f): стек, как обычно, имеет высокие адреса, в то время как память, используемая для брошенного объекта, в виртуальном адресном пространстве довольно низка. Тем не менее, это деталь реализации, поскольку, как указывалось в других ответах, в стандарте ничего не говорится о том, где должна быть память для брошенного объекта и как она должна быть выделена.

6
ответ дан 29 November 2019 в 01:32
поделиться

В дополнение к тому, что стандарт говорит в 15.1 / 4 («Обработка исключений / Создание исключения») - что память для временной копии исключения выбрасываемый объект распределяется неопределенным образом - пара других мелочей о том, как выделяется объект исключения:

  • 3.7.3.1/4 («Функции распределения») стандарта указывает, что объект исключения не может быть выделяется выражением new или вызовом «глобальной функции распределения» (т. е. заменой оператора new () ). Обратите внимание, что malloc () не является «глобальной функцией распределения», как определено стандартом, поэтому malloc () определенно является вариантом для выделения объекта исключения.

  • «Когда генерируется исключение, создается объект исключения и обычно помещается в какой-то стек данных исключения» (Стэнли Липпман, «Внутри объектной модели C ++» - 7.2 Обработка исключений)

  • Из книги Страуструпа «Язык программирования C ++, 3-е издание»: «Реализация C ++ должна иметь достаточно свободной памяти, чтобы иметь возможность выбросить bad_alloc в случае исчерпания памяти. Однако, возможно, что выброс какого-либо другого исключения вызовет исчерпание памяти. »(14.4.5 Исчерпание ресурсов); и «Реализация может применять широкий спектр стратегий для хранения и передачи исключений. Однако гарантируется, что имеется достаточно памяти, чтобы позволить new выдать стандартное исключение нехватки памяти, bad_alloc "(14.3 Перехват исключений).

Обратите внимание, что цитаты Страуструпа являются предварительными. Мне кажется интересным, что стандарт, похоже, не дает гарантии того, что Страуструп считал достаточно важным, чтобы упоминать его дважды.

4
ответ дан 29 November 2019 в 01:32
поделиться
Другие вопросы по тегам:

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