Как реализована среда обработки исключений в C ++? - PullRequest
75 голосов
/ 29 января 2009

Я заинтригован тем, как работает механизм обработки исключений в C ++. В частности, где хранится объект исключения и как он распространяется в нескольких областях, пока не будет перехвачен? Хранится ли он в какой-то глобальной области?

Поскольку это может зависеть от компилятора, может кто-нибудь объяснить это в контексте набора компиляторов g ++?

Ответы [ 4 ]

42 голосов
/ 29 января 2009

Реализации могут отличаться, но есть некоторые основные идеи, которые вытекают из требований.

Сам объект исключения - это объект, созданный в одной функции и уничтоженный в вызывающей стороне. Следовательно, обычно невозможно создать объект в стеке. С другой стороны, многие объекты исключений не очень большие. Например, можно создать, например, 32-байтовый буфер и переполнить до кучи, если на самом деле нужен объект большего размера.

Что касается фактической передачи контроля, существуют две стратегии. Одним из них является запись достаточного количества информации в самом стеке, чтобы разматывать стек. В основном это список деструкторов для запуска и обработчиков исключений, которые могут перехватить исключение. Когда возникает исключение, запускайте стек, выполняя эти деструкторы, пока не найдете соответствующий улов.

Вторая стратегия перемещает эту информацию в таблицы вне стека. Теперь, когда возникает исключение, стек вызовов используется, чтобы выяснить, какие области вводятся, но не выходятся. Затем они просматриваются в статических таблицах, чтобы определить, где будет обработано выброшенное исключение, и какие деструкторы выполняются между ними. Это означает, что в стеке меньше издержек на исключение; обратные адреса нужны в любом случае. Таблицы являются дополнительными данными, но компилятор может поместить их в загруженный по требованию сегмент программы.

18 голосов
/ 29 января 2009

Это определено в 15.1. Исключение из стандарта.

Бросок создает временный объект.
Как распределяется память для этого временного объекта, не указано.

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

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

Примечание: Если вы ловите по ссылке, ссылка будет ссылаться на временную, Если вы ловите по значению, временный объект копируется в значение (и, следовательно, требуется конструктор копирования).

Совет от С.Мейерса (Catch by const reference).

try
{
    // do stuff
}
catch(MyException const& x)
{
}
catch(std::exception const& x)
{
}
12 голосов
/ 29 января 2009

Вы можете посмотреть здесь для подробного объяснения.

Может также помочь взглянуть на прием, используемый в простом C для реализации некоторого базового вида обработки исключений. Это влечет за собой использование setjmp () и longjmp () следующим образом: первый сохраняет стек, чтобы пометить обработчик исключений (например, «catch»), в то время как последний используется для «выброса» значения. «Брошенное» значение выглядит так, как будто оно было возвращено из вызываемой функции. «Блок try» заканчивается, когда setjmp () вызывается снова или когда функция возвращается.

8 голосов
/ 22 августа 2012

Я знаю, что это старый вопрос, но есть очень хорошее изложение, объясняющее оба метода, используемых в каждом из gcc и VC, здесь:

...