Что произойдет, если я не поймаю броска? - программирование
Подтвердить что ты не робот

Что произойдет, если я не поймаю броска?

Это супер базовый, но я не могу найти ответ нигде. Там много сообщений о метании и ловушке, но что на самом деле происходит, если я 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!

4b9b3361

Ответ 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.