Пространства имён
Варианты
Действия

Выражение throw

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++17*)
Спецификатор noexcept (C++11)
Исключения
Выражение throw
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определяемые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Types
Объявление typedef
Объявление псевдонима типа (C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
Выражения
Общие
Категории значений (lvalue, rvalue, xvalue)
Порядок оценки (точки последовательности)
Константные выражения
Потенциально оцениваемые выражения
Первичные выражения
Лямбда-выражения(C++11)
Литералы
Целочисленные литералы
Литералы с плавающей запятой
Логические литералы
Символьные литералы, включая управляющие последовательности
Строковые литералы
Литерал нулевого указателя(C++11)
Пользовательский литерал(C++11)
Операторы
a=b, a+=b, a-=b, a*=b, a/=b, a%=b, a&=b, a|=b, a^=b, a<<=b, a>>=b
++a, --a, a++, a--
+a, -a, a+b, a-b, a*b, a/b, a%b, ~a, a&b, a|b, a^b, a<<b, a>>b
a||b, a&&b, !a
a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b (начиная с C++20)
a[b], *a, &a, a->b, a.b, a->*b, a.*b
a(...), a,b, a?b:c
выражение new
выражение delete
выражение throw
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
Выражения свёртки(C++17)
Альтернативные представления операторов
Приоритет и ассоциативность
Перегрузка операторов
Сравнение по умолчанию(C++20)
Преобразования
Неявные преобразования
Обычные арифметические преобразования
const_cast
static_cast
reinterpret_cast
dynamic_cast
Явные преобразования: (T)a, T(a), auto(a), auto{a} (начиная с C++23)
Пользовательское преобразование
 
 

Сигнализирует об ошибочном условии и выполняет обработчик ошибок.

Содержание

[править] Синтаксис

throw выражение (1)
throw (2)

[править] Объяснение

Смотрите блок try-catch для получения дополнительной информации о блоках (обработчиках исключений) try и catch
1) Во-первых, инициализирует копированием объект исключения из выражения
  • Это может вызвать конструктор перемещения для выражения rvalue. Даже если инициализация копированием выбирает конструктор перемещения, инициализация копированием из lvalue должна быть правильно сформирована, а деструктор должен быть доступен.
(начиная с C++11)
  • Это может также вызвать конструктор перемещения для выражений, которые именуют локальные переменные или функции или параметры предложения catch, область действия которых не выходит за пределы самого внутреннего включающего блока try (если есть), с тем же разрешением перегрузки, что и в операторе return
(начиная с C++17)
затем управление передаётся обработчику исключений с совпадающим типом, для которого составной оператор или список инициализаторов элементов, следующий за ключевым словом try, был введён последним и не был завершён этим потоком исполнения.
2) Повторно выдаёт текущее обработанное исключение. Отказывается от выполнения текущего блока catch и передаёт управление следующему подходящему обработчику исключений (но не другому предложению catch после того же блока try: его составной оператор считается завершённым), повторно используя существующий объект исключения: новые объекты не создаются. Эта форма разрешена только тогда, когда в данный момент обрабатывается исключение (она вызывает std::terminate, если используется иначе). Предложение catch, связанное с блоком-try-функции, должно завершиться путём повторного создания, если оно используется в конструкторе.

Смотрите std::terminate и std::unexpected (до C++17) для обработки ошибок, возникающих при обработке исключений.

[править] Объект исключения

Объект исключения это временный объект в неуказанной области памяти, созданный с помощью выражения throw.

Преобразования массива в указатель и функции в указатель выполняются в выражении. Тип объекта исключения определяется путём удаления всех cv-квалификаторов верхнего уровня из типа выражения (возможно, после преобразований), а объект исключения инициализируется копированием из выражения возможно, после преобразований).

Если тип объекта исключения будет неполным типом, типом абстрактного класса или указателем на неполный тип, отличный от указателя на (возможно, cv-квалифицированный) void, программа некорректна.

Если тип выражения является классовым типом, его конструктор копирования/перемещения (начиная с C++11) и деструктор должны быть доступны, даже если происходит пропуск копирования.

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

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

[править] Раскручивание стека

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

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

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

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

(начиная с C++11)

Если исключение выдаётся из конструктора, который вызывается выражением new, вызывается соответствующая функция освобождения памяти, если она доступна.

Этот процесс называется раскручиванием стека.

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

Если исключение сгенерировано и не перехвачено, включая исключения, которые вызываются из начальной функцию std::thread, функции main и конструктора или деструктора любых статических или локальных объектов потока, тогда вызывается 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("слишком большой") : 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 << ") построен успешно\n"; }
    ~A() { std::cout << "A(" << n << ") уничтожен\n"; }
};
 
int foo()
{
    throw std::runtime_error("ошибка");
}
 
struct B
{
    A a1, a2, a3;
 
    B() try : a1(1), a2(foo()), a3(3)
    {
        std::cout << "B пос��роен успешно\n";
    }
    catch(...)
    {
    	std::cout << "B::B() завершается исключением\n";
    }
 
    ~B() { std::cout << "B уничтожен\n"; }
};
 
struct C : A, B
{
    C() try
    {
        std::cout << "C::C() завершён успешно\n";
    }
    catch(...)
    {
        std::cout << "C::C() завершается исключением\n";
    }
 
    ~C() { std::cout << "C уничтожен\n"; }
};
 
int main () try
{
    // создаёт базовый подобъект A
    // создаёт элемент a1 класса B
    // не удаётся создать элемент a2 класса B
    // раскручивание уничтожает элемент a1 из B
    // раскручивание уничтожает базовый подобъект A
    C c;
}
catch (const std::exception& e)
{
    std::cout << "main() не удалось создать C с: " << e.what();
}

Вывод:

A(0) построен успешно
A(1) построен успешно
A(1) уничтожен
B::B() завершается исключением
A(0) уничтожен
C::C() завершается исключением
main() не удалось создать C с: ошибка

[править] Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
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 не указан источник копирования-инициализации объекта
исключения
инициализируется копированием из выражения

[править] Смотрите также