Как выбросить исключение в c
Перейти к содержимому

Как выбросить исключение в c

  • автор:

Исключения. Перехват и удаление исключений

В следующих инструкциях и примерах показано, как перехватывать и удалять исключения. Дополнительные сведения о try функциях и catch throw ключевое слово см. в рекомендациях по использованию современных C++ для исключений и обработки ошибок.

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

Блок catch должен удалить исключение, если:

    Блок catch создает новое исключение. Конечно, при повторном вызове того же исключения не следует удалять:

catch (CException* e) < if (m_bThrowExceptionAgain) throw; // Do not delete e else e->Delete(); > 

При удалении CException элемента используйте Delete функцию-член для удаления исключения. Не используйте delete ключевое слово, так как это может завершиться ошибкой, если исключение не находится в куче.

Перехват и удаление исключений
  1. try Используйте ключевое слово для настройки try блока. Выполните любые инструкции программы, которые могут вызвать исключение в блоке try . catch Используйте ключевое слово для настройки catch блока. Поместите код обработки исключений catch в блок. Код в блоке выполняется только в том случае, если код в catchtry блоке создает исключение типа, указанного в инструкции catch . В следующем скелете показано, как try и catch блоки обычно упорядочены:
try < // Execute some code that might throw an exception. AfxThrowUserException(); >catch (CException* e) < // Handle the exception here. // "e" contains information about the exception. e->Delete(); > 

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

try < // Execute some code that might throw an exception. AfxThrowUserException(); >catch (CMemoryException* e) < // Handle the out-of-memory exception here. e->Delete(); > catch (CFileException* e) < // Handle the file exceptions here. e->Delete(); > catch (CException* e) < // Handle all other types of exceptions here. e->Delete(); > 

Операторы try, throw и catch (C++)

Для реализации обработки исключений в C++используется try throw и catch выражения.

Во-первых, используйте try блок для заключения одного или нескольких операторов, которые могут вызвать исключение.

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

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

В этом примере показан try блок и его обработчики. Предположим, GetNetworkResource() получает данные через сетевое подключение, а 2 типа исключений являются определенными пользователем классами, производными от std::exception . Обратите внимание, что исключения перехватываются по const ссылке в инструкции catch . Рекомендуется создавать исключения по значению и захватывать их ссылкой константы.

Пример

MyData md; try < // Code that could throw an exception md = GetNetworkResource(); >catch (const networkIOException& e) < // Code that executes when an exception of type // networkIOException is thrown in the try block // . // Log error message in the exception object cerr catch (const myDataFormatException& e) < // Code that handles another exception type // . cerr // The following syntax shows a throw expression MyData GetNetworkResource() < // . if (IOSuccess == false) throw networkIOException("Unable to connect"); // . if (readError) throw myDataFormatException("Format error"); // . >

Замечания

Код после try предложения является защищенным разделом кода. Выражение throw вызывает исключение. Блок кода после catch предложения является обработчиком исключений. Это обработчик, который перехватывает исключение, которое создается, если типы в throw выражениях catch совместимы. Список правил, которые управляют сопоставлением типов в catch блоках, см. в разделе «Оценка блоков catch». catch Если инструкция задает многоточие (. ) вместо типа, catch блок обрабатывает каждый тип исключения. При компиляции с /EHa параметром они могут включать структурированные исключения C и системные или асинхронные исключения, созданные приложением, такие как защита памяти, разделение на ноль и нарушения с плавающей запятой. Так как catch блоки обрабатываются в программе, чтобы найти соответствующий тип, обработчик многоточия должен быть последним обработчиком для связанного try блока. Используйте с осторожностью; не позволяйте catch(. ) программе продолжать работу, если блок catch не знает, как обрабатывать определенное исключение, которое поймано. Как правило, блок catch(. ) используется для ведения журнала ошибок и выполнения специальной очистки перед остановкой выполнения программы.

Выражение throw , которое не имеет операнда повторно выполняет обработку исключения. Мы рекомендуем эту форму при повторном создании исключения, так как это сохраняет сведения о полиморфном типе исходного исключения. Такое выражение должно использоваться только в catch обработчике или в функции, которая вызывается из обработчика catch . Объект исключения rethrown является исходным объектом исключения, а не копией.

try < throw CSomeOtherException(); >catch(. ) < // Catch all exceptions - dangerous. // Respond (perhaps only partially) to the exception, then // re-throw to pass the exception to some other handler // . throw; >

Исключения

В процессе работы программы могут возникать различные ошибки. Например, при передаче файла по сети оборвется сетевое подключение или будут введены некорректные и недопустимые данные, которые вызовут падение программы. Такие ошибки еще называются исключениями. Исключение представлякт временный объект любого типа, который используется для сигнализации об ошибке. Цель объекта-исключения состоит в том, чтобы передать информацию из точки, в которой произошла ошибка, в код, который должен ее обработать. Если исключение не обработано, то при его возникновении программа прекращает свою работу.

Например, в следующей программе происходит деление чисел:

#include double divide(int a, int b) < return a / b; >int main() < int x; int y<>; double z ; std::cout

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

С одной стороны, мы можем в функции divide определить проверку и выполнять деление, если параметр b не равен 0. Однако нам в любом случае надо возвращать из функции divide некоторый результат — некоторое число. То есть мы не можем просто написать:

double divide(int a, int b) < if (b) return a / b; else std::cout

И в этом случае нам надо известить систему о возникшей ошибке. Для этого используется оператор throw .

Оператор throw генерирует исключение. Через оператор throw можно передать информацию об ошибке. Например, функция divide могла бы выглядеть следующим образом:

double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >

То есть если параметр b равен 0, то генерируем исключение.

Но это исключение еще надо обработать в коде, где будет вызываться функция divide. Для обработки исключений применяется конструкция try. catch . Она имеет следующую форму:

try < инструкции, которые могут вызвать исключение >catch(объявление_исключения)

В блок кода после ключевого слова try помещается код, который потенциально может сгенерировать исключение.

После ключевого слова catch в скобках идет параметр, который передает информацию об исключении. Затем в блоке производится собственно обработка исключения.

Так изменим весь код следующим образом:

#include double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >int main() < int x; int y<>; try < double z ; std::cout catch (. ) < std::cout std::cout

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

В блоке catch идет обработка исключения. Причем многоточие в скобках после оператора catch ( catch(. ) ) позволяет обработать любое исключение.

В итоге когда выполнение программы дойдет до строки

double z ;

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

Error! The End.

Однако в данном случае мы только знаем, что произошла какая-то ошибка, а какая именно, неизвестно. Поэтому через параметр в блоке catch мы можем получить то сообщение, которое передается оператору throw :

#include >iostream < double divide(int a, int b) < if (b) return a / b; throw "Division by zero!"; >int main() < int x; int y<>; try < double z ; std::cout catch (const char* error_message) < std::cout std::cout

С помощью параметра const char* error_message получаем сообщение, которое предано оператору throw, и выводим это сообщение на консоль. Почему здесь мы получаем сообщение об ошибке в виде типа const char* ? Потому что после оператора throw идет строковый литерал, который представляет как раз тип const char* . И в этом случае консольный вывод будет выглядеть следующим образом:

Division by zero! The End.

Таким образом, мы можем узнать суть возникшего исключения. Подобным образом мы можем передавать информацию об исключении через любые типы, например, std::string:

throw std::string;

Тогда в блоке catch мы можем получить эту информацию в виде объекта std::string :

catch (std::string error_message)

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

Существует очень много функций и в стандартной библиотеке С++, и в каких-то сторонних библиотеках. И может возникнуть вопрос, какие из них вызывать в конструкции try-catch, чтобы не столкнуться с необработанным исключением и аварийным завершением программы. В этом случае может помочь прежде всего документация по функции (при ее наличии). Другой сигнал — ключевое слово noexcept , которое при использовании в заголовке функции указывает, что эта функция никогда не будет генерировать исключения. Например:

void print(int argument) noexcept;

Здесь указываем, что функция print() никогда не вызовет исключение. Таким образом, встретив функцию с подобным ключевым словом, можно ожидать, что она не вызовет исключения. И соответственно нет необходимости помещать ее вызов в конструкцию try-catch.

Создание объекта исключения

При обработке исключения стоит помнить, что при передаче объекта оператору throw блок catch получает копию этого объекта. И эта копия существует только в пределах блока catch.

Для значений примитивных типов, например, int , копирование значения может не влиять на производительность программы. Однако при передаче объектов классов издержки могут выше. Поэтому в этом случае объекты обычно передаются по ссылке, например:

#include double divide(int a, int b) < if (b) return a / b; throw std::string; > int main() < int x; int y<>; try < double z ; std::cout catch (const std::string& error_message) // строка передается по ссылке < std::cout std::cout

Обработка и генерация разных типов исключений

Мы можем генерировать и обрабатывать несколько разных исключительных ситуаций. Допустим, нам надо, чтобы при делении делитель (второе число) был не больше, чем делимое (первое число):

#include double divide(int a, int b) < if(!b) // если b == 0 < throw 0; >if(b > a) < throw "The second number is greater than the first one"; >return a / b; > void test(int a, int b) < try < double result ; std::cout catch (int code) < std::cout catch (const char* error_message) < std::cout > int main() < test(100, 20); // 5 test(100, 0); // Error code: 0 test(100, 1000); // The second number is greater than the first one >

В функции divide в зависимости от значения числа b оператору throw передаем либо число:

throw 0;

либо строковый литерал:

throw "The second number is greater than the first one";

Для тестирования функции divide определена другая функция — test, где вызов функции divide() помещен в конструкцию try..catch . Поскольку при генерации исключения мы можем получать ошибку в виде двух типов — int (если b равно 0) и const char* (если b больше a), то для обработки каждого типа исключений определены два разных блока catch:

catch (int code) < std::cout catch (const char* error_message)

В функции main вызываем функцию test, передавая в нее различные числа. При вызове:

test(100, 20); // 5

число b не равно 0 и меньше a, поэтому никаких исключений не возникает, блок try срабатывает до конца, и функция завершает свое выполнение.

При втором вызове

test(100, 0); // Error code: 0

число b равно 0, поэтому генерируется исключение, а в качестве объекта исключения передается число 0. Поэтому при возникновении исключения программа выберет тот блок catch, где обрабатывается исключения типа int:

catch (int code)

При третьем вызове

test(100, 1000); // The second number is greater than the first one

число b больше a, поэтому объект исключения будет представлять строковый литерал или const char* . Поэтому при возникновении исключения программа выберет блок catch, где обрабатывается исключения типа const char*:

catch (const char* error_message)

Таким образом, в данном случае мы получим следующий консольный вывод:

5 Error code: 0 The second number is greater than the first one

Может быть ситуация, когда генерируется исключение внутри конструкции try-catch , и даже есть блок catch для обработки исключений, однако он обрабатывает другие типы исключений:

void test(int a, int b) < try < double result ; std::cout catch (const char* error_message) < std::cout >

Здесь нет блока catch для обработки исключения типа int . Поэтому при генерации исключения:

throw 0;

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

try-catch и деструкторы

Стоит отметить, что, если в блоке try создаются некоторые объекты, то при возникновении исключения у них вызываются деструкторы. Например:

#include class Person < public: Person(std::string name) :name< name > < std::cout ~Person() < std::cout void print() < throw "Print Error"; >private: std::string name; >; int main() < try < Person tom< "Tom" >; tom.print(); // Здесь генерируется ошибка > catch (const char* error) < std::cerr >

В классе Person определяет деструктор, который выводит сообщение на консоль. В функции print просто генерируем исключение.

В функции main в блоке try создаем один объект Person и вызываем у него функцию print, что естественно приведет к генерарции исключения и переходу управления программы в блок catch . И если мы посмотрим на консольный вывод

Person Tom created Person Tom deleted Print Error

то мы увидим, что прежде чем начнется обработка исключения в блоке catch, будет вызван деструктор объекта Person.

Как выбросить исключение в c

Обычно система сама генерирует исключения при определенных ситуациях, например, при делении числа на ноль. Но язык C# также позволяет генерировать исключения вручную с помощью оператора throw . То есть с помощью этого оператора мы сами можем создать исключение и вызвать его в процессе выполнения.

Например, в нашей программе происходит ввод имени пользователя, и мы хотим, чтобы, если длина имени меньше 2 символов, то возникало исключение:

try < Console.Write("Введите имя: "); string? name = Console.ReadLine(); if (name== null || name.Length < 2) < throw new Exception("Длина имени меньше 2 символов"); >else < Console.WriteLine($"Ваше имя: "); > > catch (Exception e) < Console.WriteLine($"Ошибка: "); >

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

Затем в блоке catch сгенерированное нами исключение будет обработано.

Подобным образом мы можем генерировать исключения в любом месте программы. Но существует также и другая форма использования оператора throw, когда после данного оператора не указывается объект исключения. В подобном виде оператор throw может использоваться только в блоке catch:

try < try < Console.Write("Введите имя: "); string? name = Console.ReadLine(); if (name == null || name.Length < 2) < throw new Exception("Длина имени меньше 2 символов"); >else < Console.WriteLine($"Ваше имя: "); > > catch (Exception e) < Console.WriteLine($"Ошибка: "); throw; > > catch (Exception ex)

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *