Обработка исключений в C++
Здравствуйте, молодые программисты! Сегодня мы отправляемся в увлекательное путешествие по миру обработки исключений в C++. В качестве вашего доброго соседа по компьютерной науке, я здесь, чтобы провести вас через эту важную тему. Так что взять свой любимый напиток, устроиться комфортно и погружайтесь вместе с нами!
Что такое исключения?
Прежде чем мы начнем бросать и ловить исключения, как опытные жонглеры, давайте поймем, что такое исключения. В мире программирования исключения — это непредвиденные события, которые происходят во время выполнения программы. Они похожи на те неожиданные проверочные работы, которые я раньше давал (прости за это!) — неожиданные и иногда несколько сложные для обработки.
Исключения нарушают нормальный поток инструкций программы. Они могут быть вызваны различными факторами, такими как:
- Деление на ноль
- Доступ к массиву за пределами
- Исчерпание памяти
- Попытка открыть несуществующий файл
Теперь давайте узнаем, как C++ позволяет нам грациозно управлять этими непредвиденными ситуациями.
Бросание исключений
Основы бросания
В C++ мы используем ключевое слово throw
для вызова исключения. Это как поднять руку в классе, когда у вас есть вопрос или проблема. Вот простой пример:
#include <iostream>
using namespace std;
int main() {
try {
throw 20;
}
catch (int e) {
cout << "Произошло исключение. Номер исключения: " << e << endl;
}
return 0;
}
В этом примере мы бросаем целочисленное исключение с значением 20. Но не волнуйтесь, мы поймаем его вот-вот!
Бросание различных типов
C++ гибок и позволяет вам бросать исключения различных типов. Давайте рассмотрим более практичный пример:
#include <iostream>
#include <stdexcept>
using namespace std;
double divide(int a, int b) {
if (b == 0) {
throw runtime_error("Деление на ноль!");
}
return static_cast<double>(a) / b;
}
int main() {
try {
cout << divide(10, 2) << endl; // Это будет работать без проблем
cout << divide(10, 0) << endl; // Это вызовет исключение
}
catch (const runtime_error& e) {
cout << "Поймано исключение: " << e.what() << endl;
}
return 0;
}
В этом примере мы бросаем исключение runtime_error
, когда кто-то пытается разделить на ноль. Это как установить знак "Нет деления на ноль" в нашем математическом районе!
Поймание исключений
Основы поймания
Теперь, когда мы знаем, как бросать исключения, давайте научимся их поймать. Поймание исключений — это как быть ответственным хозяином животного — вам нужно быть готовым обработать все, что ваш код бросит вам!
Мы используем блок try-catch
для поймания исключений. Блок try
содержит код, который может бросить исключение, а блок catch
обрабатывает исключение, если оно возникает.
#include <iostream>
using namespace std;
int main() {
try {
int age = -5;
if (age < 0) {
throw "Возраст не может быть отрицательным!";
}
cout << "Возраст: " << age << endl;
}
catch (const char* msg) {
cerr << "Ошибка: " << msg << endl;
}
return 0;
}
В этом примере мы проверяем, является ли возраст отрицательным. Если да, мы бросаем исключение с пользовательским сообщением об ошибке.
Поймание нескольких исключений
Иногда из одного фрагмента кода могут быть брошены различные типы исключений. В таких случаях у нас может быть несколько блоков catch:
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
try {
int choice;
cout << "Введите 1 для целочисленного исключения, 2 для ошибки времени выполнения: ";
cin >> choice;
if (choice == 1) {
throw 42;
} else if (choice == 2) {
throw runtime_error("Появилась дикая ошибка времени выполнения!");
} else {
throw "Неизвестный выбор!";
}
}
catch (int e) {
cout << "Поймано целочисленное исключение: " << e << endl;
}
catch (const runtime_error& e) {
cout << "Поймана ошибка времени выполнения: " << e.what() << endl;
}
catch (...) {
cout << "Поймано неизвестное исключение!" << endl;
}
return 0;
}
Этот пример показывает, как мы можем поймать разные типы исключений. Блок catch (...)
— это универсальный, который обработает любое исключение, не пойманное предыдущими блоками catch. Это как установить сетку безопасности для всех тех неожиданных сюрпризов!
Стандартные исключения C++
C++ поставляется с набором стандартных исключений, которые вы можете использовать в своих программах. Это как швейцарские армейские ножи мира исключений — универсальные и всегда готовые к помощи!
Вот таблица некоторых часто используемых стандартных исключений:
Исключение | Описание |
---|---|
std::runtime_error | Логические ошибки времени выполнения |
std::logic_error | Логические ошибки |
std::out_of_range | Доступ за пределы диапазона |
std::overflow_error | Арифметическое переполнение |
std::bad_alloc | Сбой распределения памяти |
Посмотрим пример с использованием стандартного исключения:
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;
int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
try {
cout << numbers.at(10) << endl; // Это вызовет исключение out_of_range
}
catch (const out_of_range& e) {
cerr << "Ошибка выхода за диапазон: " << e.what() << endl;
}
return 0;
}
В этом примере мы пытаемся получить доступ к элементу, который находится за пределами диапазона в нашем векторе. Функция at()
вызывает исключение out_of_range
, когда это происходит.
Определение новых исключений
Хотя стандартные исключения отличные, иногда вам нужно что-то более специфическое для ваших конкретных нужд. Тут на помощь приходят пользовательские исключения!
Вот как можно определить свой собственный класс исключения:
#include <iostream>
#include <exception>
using namespace std;
class NegativeValueException : public exception {
public:
const char* what() const throw() {
return "Отрицательные значения не допускаются!";
}
};
double squareRoot(double x) {
if (x < 0) {
throw NegativeValueException();
}
return sqrt(x);
}
int main() {
try {
cout << squareRoot(25) << endl; // Это будет работать без проблем
cout << squareRoot(-5) << endl; // Это вызовет наше пользовательское исключение
}
catch (const NegativeValueException& e) {
cerr << "Ошибка: " << e.what() << endl;
}
return 0;
}
В этом примере мы создали пользовательский класс NegativeValueException
. Мы используем его в нашей функции squareRoot
, чтобы бросить исключение, когда кто-то пытается вычислить квадратный корень отрицательного числа.
Итак, это всё, друзья! Мы осветили основы обработки исключений в C++. Помните, исключения — это ваши друзья. Они помогают вам писать более устойчивый и менее подверженный ошибкам код. Постоянно практикуйтесь, и скоро вы будете обрабатывать исключения как профи!
Счастливого кодирования, и пусть ваши исключения всегда будут пойманы!
Credits: Image by storyset