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

Как исключения, выделенные в стеке, выходят за рамки их возможностей?

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

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

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

Я добавил инструкцию print для дескриптора SomeKindOfException, и он показывает, что ex разрушен, когда он выходит из области видимости в f(), но затем он попадает в g ​​() и снова разрушается после того, как он выходит из области видимости,

Любая помощь?

4b9b3361

Ответ 1

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

Ответ 2

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

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

Ответ 3

С++ Standard 15.1/4:

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

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

Ответ 4

Когда вы выбрасываете 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;
}

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

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

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

Ответ 5

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

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

  • "Когда генерируется исключение, создается объект исключения и помещается, как правило, в какой-либо стек данных исключений" (Stanley Lippman, "Inside the С++ Object Model" - 7.2 Обработка исключений)

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

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

Ответ 6

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