# 11 лекция - C++ (07.05.2022) ## Исключения В си для обработок ошибок приняты коды возврата. ```c float strtonnm(conat char *); //функция для преобразования строку в число //как вернуть ошибку? struct result strtonnm(conat char *); //вернуть структуру (реже всего) float strtonnm(conat char * x, int *error); //принять указатель на переменную означающую ошибку (встречается чаще) { if (x == NULL) { *error = 1; //в качестве error нельзя давать null return 0; //возвращает некий ноль } //... } int strtonnm(conat char * x, int *error); //поменять местами значение и переменную ошибки { if(!x) //x==NULL - тоже самое { if(error) //делаем параметр error опциональным *error = 1; return 0; } } int err; float f = strtonnm(argv[1], &err); if (err != 0) { //... } ``` Какие коды использовать? Есть 3.5 подхода: * Использовать bool, `true` - всё хорошо, `false` - всё плохо * Использовать int, `0` - всё плохо, `НЕ 0` - всё хорошо * Использовать signed int, `>=0` - всё хорошо, `<0` - всё плохо * ?? В C++ сделали исключения ```c++ float strtonnm(const char *x) { if (!x) throw "NULL pointer"; //бросить ошибку в окружающее пространство //... } int main(int argc, char **argv) { try { float f = strtonnm(argv[1]); //если обратились в следующий аргумент после последнего выхода за границы не будет - спец случай } catch (int x) //здесь ловит int //можно писать даже без названия переменной, если не интересно значение переменной { //... } catch (const char *y) //здесь ловит const char * (костантные строки) { throw y; //бросить дальше //... } catch (...) //ловить всё, что не поймали выше //нельзя узнать значение, очевидно { throw; //бросить дальше, даже если непонятно что приняли //throw Без аргументов только для блоков catch } //... } ``` > `catch` проверяется последовательно > Если написать `catch` по базовому классу, то он ещё поймает ошибку с классом наследником > Все стандартные плюсовые библиотеки бросают ошибки Есть проблема с тем, что ошибка будет пробрассываться вверх до блока `catсh`, и динамические обьекты не будут освобожденны > При бросание ошибки вызываются деструкторы локальных обьектов (не динамических) > Привязывать освобождение обьектов надо к деструктору, `RAII` - концепция ### Минусы 1) Не бесплатно, даже если нет иключения 2) ??? 3) Функция может бросить что угодно (в новом стандарте `noexcept`, обещание не бросать, если броситься - программа сразу `undefine behavier`) (позволяет компилятору оптимизировать код) 4) Довольно сильно ограничивает оптимизатор ```c++ int main() noexcept { //... } ``` > Компятор может скомпилировать только исключения брошенные кодом скомпилированным от него же... > `try` не ловит исключения деления на `0`, обращение сквользь `NULL`, только то, что бросили через `throw` ```c++ float strtonnm(const char *x) try { //... } catch() { //... } ``` > Так можно поймать исключения конструктора внутри класса > в конструктор не сможет так поймат исключение ```c++ class A { int f() const; mutable int x; //могут менять методы const } ``` `int *arr = new(std::nothrow) int[10];` (заголовочный файл `include <new>`)