исключения - это чисто runtime штука
у функций есть 4 типа гарантий exception safety:
- nothrow - функция никогда не кидает исключения (noexcept). Noexcept ожидается от деструкторов, свапов и мув конструкторов
- strong - если функция кинула исключение, то состояние объектов возвращается к тому виду, какое оно было до вызова этой функции (пример: пушбек у вектора)
- basic - если функция кинула исключение, то программа остается в валидном состоянии. ресурсы не утекают и инварианты сохраняются
- no - при выбросе исключения могут утечь ресурсы и объекты могут оказаться в несогласованном состоянии
прикольный способ проверить strong exception safety - мысленно провести линию если выше неё не меняется состояние класса, а ниже неё не бросаются исключения, то гарантия будет строгой


throw - альтернативный способ выйти из функции, наряду с return можно throw любой expression, но обычно кидают объект std::exception объект, который бросаем будет всегда lvalue после throw происходит раскрутка стека - уничтожаются локальные переменные, вызываются деструкторы, пока не выскочим на уровень, на котором был вызван catch нужного типа. exception.what() возвращает строку, которую передали, когда создавали объект
ловить исключения - дорогая по времени операция
операторы new и delete бросают исключения а ещё dynamic_cast может кидать исключения а еще throw кидает исключения а ещё typeid()
std::terminate вызывается автоматически в runtime, если исключение брошено и не поймано
difference between exceptions and runtime errors
поймать кетчем можно только исключения уровня c++. если сделать runtime ошибку (ex. segfault) - то будет RE а если обернуть это в try catch, то ниче не будет, segfault это не exception!!!! floating point exception - не exception!! это исключение уровня процессора aborted - тоже исключение уровня процессора stackoverflow аналогично все сверху RE - эксепшены с точки зрения процессора, но не плюсов, поэтому try catchem не ловятся
есть класс std::runtime_error - частный случай std::exception
эксепшены уровня плюсов и эксепшены-рантайм эрроры, из-за которых прога падает - разные уровни ошибок, первые можно поймать (если не поймаем - aborted), а вторые поймать нельзя
иерархия плюсовых эксепшенов:
- logic error - виноват пользователь
- runtime error - невиноват пользователь
можно бросать (throw) что угодно при throw копируется, кладётся в динамическую память (не задокументировано стандартом). старый объект снимается со стека то есть накладные расходы - выделение дин. памяти. оч дорого
emergency buffer - под std::bad_alloc есть место в статической памяти, где будет храниться exception (очев, что не в динамической его создавать, тк она кончилась)
из блока catch можно опять сделать throw (в том числе без аргумента) без аргументов - пускать лететь выше то, что летело а если thow что-то, то тот объект, что поймали уничтожается, а вместо него создается новый если сделать throw из catch’a, то его могут поймать только catch’и уровнем выше
формально если исключение не ловится, то компилятор ничего не гарантирует, кроме падения из-за терминейт. нет гарантии вызова деструкторов, крч UB
на выбор типа catch не применяются правила перегрузки. если нету подходящего типа без приведения, то terminate. выбирается первый подходящий catch но
- конверсии в const делаются
- а еще можно ловить по родителю наследника (если наследование публичное - мы из других функций имеем возможность знать о наследовании) (и если он вообще может кастануть - нет ambigous cast)
эксепшены в конструкторах ведут к невызову деструктора класса (деструкторы полей вызовутся)
если конструктор кидает исключения, то они могут произойти в списке инициализации. будут вызваны деструкторы уже созданных полей.
обработка исключения, возникающего в списке инициализации через try в скоупе класса - function try block
S(int x) try: a(1), aa(0), aaa(3) {
std::cout << 5;
} catch (...) {}
catch после тела конструктора
в самом конце сигнатуры любой функции можно написать try на всё тело функции
исключения в деструкторах это зло потому что деструктор мог быть вызван тем, что произошло исключение, тогда вызывается std::terminate с сообщением, что было проброшено исключение из деструктора - до C++11 с C++11 вообще нельзя бросать исключения из деструктора (если не написать noexcept(false)) но если уже летит одно исключение, то terminate
есть спецификатор noexcept, который мы пишем справа от сигнатуры функции, чтобы пометить, что функция не бросает исключения (чтоб компилятор смог оптимизировать, и если всё таки вылетит exception из noexcept - RE)
и есть оператор noexcept(expression), который возвращает true, если выражение noexcept
НЕ noexcept:
- dynamic_cast
- new/delete
- throw
- typeid()
- НЕ noexcept funcs
как проверить летит ли прямо сейчас exception?
bool std::uncaught_exception() - реализована компилятором (c С++17 depricated, C++20 removed, потому что бывает, что в рантайме летит одновременно несколько исключений)
c C++17 - int std::uncaught_exceptions()
возвращает сколько исключений в данный момент летят в одном потоке
когда бросается rvalue при возможности вызывается мув конструктор
если у объекта, который мы кидаем нет copy ctor, то его нельзя бросить, тк если мы кидаем локальный объект, то делаем его копию и её кидаем
можно навесть try/catch на member initializer list
