Namespaces
Variants

Throwing exceptions

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
throw -expression
try block
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Выбрасывание исключения передает управление обработчику .

Исключение может быть выброшено из throw выражений , следующие контексты также могут выбрасывать исключения:

Содержание

Объект исключения

Выбрасывание исключения инициализирует объект с динамическим storage duration , называемый exception object .

Если тип объекта исключения будет одним из следующих типов, программа является некорректной:

Конструирование и уничтожение объектов исключений

Учитывая тип объекта исключения как T :

Память для объекта исключения выделяется неопределённым образом. Единственная гарантия заключается в том, что хранилище никогда не будет выделено глобальными функциями выделения памяти .

Если обработчик завершается посредством повторного возбуждения , управление передается другому обработчику для того же объекта исключения. В этом случае объект исключения не уничтожается.

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

Уничтожение происходит сразу после уничтожения объекта, объявленного в "списке параметров" обработчика.

(until C++11)

Точки потенциального уничтожения для объекта исключения:

  • Когда активный обработчик исключения завершает работу любым способом, кроме повторного выброса, сразу после уничтожения объекта (если есть), объявленного в "списке параметров" обработчика.
  • Когда объект типа std::exception_ptr , который ссылается на объект исключения, уничтожается, до возврата из деструктора std::exception_ptr .

Среди всех точек потенциального уничтожения объекта исключения существует неопределенная последняя точка, где объект исключения уничтожается. Все остальные точки happen before этой последней. Реализация может затем освободить память для объекта исключения неопределенным образом.

(since C++11)

throw выражения

throw expression (1)
throw (2)
Перевод:
throw выражение (1)
throw (2)
1) Выбрасывает новое исключение.
2) Повторно возбуждает текущее обрабатываемое исключение.
expression - выражение, используемое для создания объекта исключения


Когда выбрасывается новое исключение, его объект исключения определяется следующим образом:

  1. Стандартные преобразования array-to-pointer и function-to-pointer применяются к expression .
  2. Пусть ex будет результатом преобразования:
  • Тип объекта исключения определяется путем удаления всех cv-квалификаторов верхнего уровня из типа ex .
  • Объект исключения copy-initialized из ex .

Если программа пытается повторно выбросить исключение, когда в данный момент не обрабатывается ни одного исключения, std::terminate будет вызван. В противном случае исключение реактивируется с существующим объектом исключения (новый объект исключения не создаётся), и исключение больше не считается перехваченным.

try
{
    // выбрасываем новое исключение 123
    throw 123;
}
catch (...) // перехватываем все исключения
{
    // обрабатываем (частично) исключение 123
    throw; // передаем исключение другому обработчику
}

Раскрутка стека

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

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

Если исключение выбрасывается из конструктора или (редко) из деструктора объекта (независимо от продолжительности хранения объекта), деструкторы вызываются для всех полностью сконструированных нестатических невариантных членов и базовых классов в обратном порядке завершения их конструкторов. Вариантные члены union-like классов уничтожаются только в случае раскрутки стека из конструктора, и если активный член изменился между инициализацией и уничтожением, поведение не определено.

Если делегирующий конструктор завершается исключением после успешного завершения неделегирующего конструктора, вызывается деструктор для этого объекта.

(since C++11)

Если исключение выбрасывается из конструктора, который вызывается new-expression , вызывается соответствующая deallocation function , если она доступна.

Этот процесс называется stack unwinding .

Если любая функция, вызываемая непосредственно механизмом раскрутки стека, после инициализации объекта исключения и до начала обработчика исключения, завершается с исключением, std::terminate вызывается. К таким функциям относятся деструкторы объектов с автоматической продолжительностью хранения, чьи области видимости покидаются, и конструктор копирования объекта исключения, который вызывается (если не исключен ) для инициализации аргументов перехвата по значению.

Если исключение было выброшено и не перехвачено, включая исключения, покидающие начальную функцию std::thread , функцию main, а также конструктор или деструктор любых статических или thread-local объектов, то std::terminate вызывается. Определяется реализацией, происходит ли какое-либо раскручивание стека для неперехваченных исключений.

Примечания

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

try
{
    std::string("abc").substr(10); // выбрасывает std::out_of_range
}
catch (const std::exception& e)
{
    std::cout << e.what() << '\n';
//  throw e; // копирующая инициализация нового объекта исключения типа std::exception
    throw;   // повторно выбрасывает объект исключения типа std::out_of_range
}

Выражение throw классифицируется как prvalue выражение типа void . Как и любое другое выражение, оно может быть подвыражением в другом выражении, чаще всего в условном операторе :

double f(double d)
{
    return d > 1e7 ? throw std::overflow_error("too big") : d;
}
int main()
{
    try
    {
        std::cout << f(1e10) << '\n';
    }
    catch (const std::overflow_error& e)
    {
        std::cout << e.what() << '\n';
    }
}

Ключевые слова

throw

Пример

#include <iostream>
#include <stdexcept>
struct A
{
    int n;
    A(int n = 0): n(n) { std::cout << "A(" << n << ") constructed successfully\n"; }
    ~A() { std::cout << "A(" << n << ") destroyed\n"; }
};
int foo()
{
    throw std::runtime_error("error");
}
struct B
{
    A a1, a2, a3;
    B() try : a1(1), a2(foo()), a3(3)
    {
        std::cout << "B constructed successfully\n";
    }
    catch(...)
    {
        std::cout << "B::B() exiting with exception\n";
    }
    ~B() { std::cout << "B destroyed\n"; }
};
struct C : A, B
{
    C() try
    {
        std::cout << "C::C() completed successfully\n";
    }
    catch(...)
    {
        std::cout << "C::C() exiting with exception\n";
    }
    ~C() { std::cout << "C destroyed\n"; }
};
int main () try
{
    // создает базовый подобъект A
    // создает член a1 класса B
    // не удается создать член a2 класса B
    // раскрутка стека уничтожает член a1 класса B
    // раскрутка стека уничтожает базовый подобъект A
    C c;
}
catch (const std::exception& e)
{
    std::cout << "main() failed to create C with: " << e.what();
}

Вывод:

A(0) constructed successfully
A(1) constructed successfully
A(1) destroyed
B::B() exiting with exception
A(0) destroyed
C::C() exiting with exception
main() failed to create C with: error

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 499 C++98 массив с неизвестной границей не мог быть выброшен, потому что
его тип является неполным, но объект исключения может быть
создан из распавшегося указателя без каких-либо проблем
применить требование завершения типа
к объекту исключения вместо
этого
CWG 668 C++98 std::terminate не вызывался, если исключение выбрасывается
из деструктора локального неавтоматического объекта
вызывать std::terminate
в этом случае
CWG 1863 C++11 конструктор копирования не требовался для перемещаемых
объектов исключений при выбрасывании, но копирование разрешалось позже
конструктор копирования требуется
CWG 1866 C++98 вариантные члены утекали при раскрутке стека из конструктора вариантные члены уничтожаются
CWG 2176 C++98 выброс из деструктора локальной переменной
мог пропустить деструктор возвращаемого значения
возвращаемое значение функции
добавлено к раскрутке
CWG 2699 C++98 throw "EX" фактически выбрасывал char * вместо const char * исправлено
CWG 2711 C++98 источник копирующей инициализации
объекта исключения не был указан
копирующая инициализация
из выражения
CWG 2775 C++98 требование копирующей инициализации объекта исключения было неясным сделано ясным
CWG 2854 C++98 продолжительность хранения объектов исключений была неясной сделано ясным
P1825R0 C++11 неявное перемещение из параметров было запрещено в throw разрешено

Ссылки

  • Стандарт C++23 (ISO/IEC 14882:2024):
  • 7.6.18 Выражение throw [expr.throw]
  • 14.2 Генерация исключения [except.throw]
  • Стандарт C++20 (ISO/IEC 14882:2020):
  • 7.6.18 Выражение throw [expr.throw]
  • 14.2 Генерация исключения [except.throw]
  • Стандарт C++17 (ISO/IEC 14882:2017):
  • 8.17 Выражение throw [expr.throw]
  • 18.1 Генерация исключения [except.throw]
  • Стандарт C++14 (ISO/IEC 14882:2014):
  • 15.1 Генерация исключения [except.throw]
  • Стандарт C++11 (ISO/IEC 14882:2011):
  • 15.1 Выбрасывание исключения [except.throw]
  • Стандарт C++03 (ISO/IEC 14882:2003):
  • 15.1 Генерация исключения [except.throw]
  • Стандарт C++98 (ISO/IEC 14882:1998):
  • 15.1 Генерация исключения [except.throw]

Смотрите также

(до C++17)