Что произойдет, если я не поймаю броска?
Это супер базовый, но я не могу найти ответ нигде. Там много сообщений о метании и ловушке, но что на самом деле происходит, если я throw
от function1
, а затем вызывается function1
из function2
, но не поймать его, означает ли это, что он просто возвращается к вызывающий function2
? Судя по следующему, я бы сказал "да", но я хотел получить солидный ответ, подобный гуру, до того, как я воюю, и предположить:
#include <iostream>
void function1()
{
throw 1;
}
void function2()
{
function1();
}
int main()
{
try
{
function2();
}
catch(...)
{
std::cout << "caught!";
return 0;
}
return 0;
}
Output:
caught!
Ответы
Ответ 1
Да, как работают исключения. Когда генерируется исключение, оно захватывается самой верхней функцией в стеке вызовов, которая имеет обработчик для этого исключения в области выполнения. Поскольку вы возвращаетесь к функции ниже в стеке, некоторые переменные в объеме функций в верхних стековых фреймах должны выйти из области видимости, и поэтому их деструкторы вызываются. Это называется распаковкой стека. Очень приятно комбинировать это и RAII (искать RAII, если вы не знаете, что это такое). Однако, если какой-либо деструктор выдает исключение во время разматывания стека, тогда он плохой, и будет вызвана функция std::terminate
. Как правило, ваша программа закончится (и именно поэтому вам всегда рекомендуется писать деструкторы без метания).
Из cppreference.com:
После создания объекта исключения поток управления работает назад (вверх по стеку вызовов), пока не достигнет начала попытки блок, в этот момент параметры связанных блоков захвата сравниваются с брошенным выражением, чтобы найти совпадение. Если нет совпадений, поток управления продолжает разматывать стек до тех пор, пока следующий блок try и т.д. Если совпадение найдено, скачки управляющего потока к соответствующему блоку catch (обработчик исключений), который выполняет в нормальном режиме.
Когда поток управления перемещается вверх по стеку вызовов, вызываются деструкторы для всех объектов с автоматической продолжительностью хранения, построенных с соответствующий блок try был введен в порядке, обратном порядку. Если исключение выбрано из конструктора, вызываются деструкторы для всех полностью построенных нестатических невариантных элементов и базы классы. Этот процесс называется отказом стека.
Ответ 2
Так как function2()
и function1()
не поймают исключения, он будет распространять стек вызовов до тех пор, пока он не будет пойман первым подходящим обработчиком, который у вас есть в main()
. Локальные деструкторы объектов вызывают по пути, который называется разворачиванием стека. Если у вас не было подходящего обработчика, среда выполнения С++ вызывала бы встроенную функцию unexpected()
, которая вызывала бы abort()
и завершала программу.
Ответ 3
Да, но он не получает "rethrown" - просто, когда вы бросаете исключение, он будет ходить в стек вызовов, пока не найдет блок catch
, который может его обработать; это одна из самых важных "точек продажи" исключений.
Если подходящий обработчик не найден, вызывается std::terminate
и ваша программа прерывается аномально (обратите внимание, что в этом случае он не гарантирует, что деструкторы будут вызваны).
Ответ 4
означает ли это, что он просто возвращается к вызывающей функции2?
Нет, он не возвращается; оригинал throw
отправляет его как можно дальше до стека вызовов до тех пор, пока не будет найден обработчик. В этом случае нет обработчика в function1
или function2
, поэтому он заканчивается в обработчике в main
.
Если он вообще не пойман и пытается выйти из main
, программа завершится. (Есть способы изменить это поведение, но это не особенно актуально для этого вопроса).
Ответ 5
Рассмотрим следующую программу:
#include <iostream>
void function1()
{
try
{
throw 1;
}
catch(...)
{
std::cout << "Exception caught in function1." << std::endl;
throw 1;
}
}
void function2()
{
try
{
function1();
}
catch(...)
{
std::cout << "Exception caught in function2." << std::endl;
throw 1;
}
}
int main()
{
try
{
function2();
}
catch(...)
{
std::cout << "Exception caught in main." << std::endl;
}
return 0;
}
Его вывод
Exception caught in function1.
Exception caught in function2.
Exception caught in main.