Глава 5. Продвинутые концепции #
5.3 Обработка исключений #
1. Базовые принципы обработки исключений #
Обработка исключений — это механизм в C++, позволяющий эффективно управлять ошибками и непредвиденными ситуациями во время выполнения программы. Основная цель механизма исключений — разделить код обработки ошибок и основную логику программы, повысив читаемость и надежность кода.
Ключевые концепции обработки исключений:
- Выявление исключительных ситуаций во время выполнения программы
- Передача информации об ошибке между различными частями программы
- Безопасный выход из аварийных состояний
- Предотвращение аварийного завершения программы
Пример базового использования: #
#include <iostream>
#include <stdexcept>
double divide(double a, double b) {
if (b == 0) {
throw std::runtime_error("Деление на ноль невозможно!");
}
return a / b;
}
int main() {
try {
double result = divide(10, 0);
std::cout << result << std::endl;
}
catch (const std::runtime_error& e) {
std::cerr << "Ошибка: " << e.what() << std::endl;
}
return 0;
}
2. Типы исключений #
C++ предоставляет широкий спектр встроенных типов исключений в стандартной библиотеке:
std::exception — базовый класс для всех стандартных исключений
std::logic_error — ошибки, которые теоретически могут быть обнаружены до выполнения программы
- std::invalid_argument
- std::domain_error
- std::length_error
std::runtime_error — ошибки, которые могут быть обнаружены только во время выполнения
- std::overflow_error
- std::underflow_error
- std::range_error
Пример использования различных типов исключений: #
#include <stdexcept>
#include <vector>
void processVector(const std::vector<int>& vec) {
if (vec.empty()) {
throw std::length_error("Вектор пуст");
}
if (vec.size() > 1000) {
throw std::range_error("Слишком большой размер вектора");
}
// Логика обработки вектора
}
3. Блоки try-catch #
Блоки try-catch
позволяют перехватывать и обрабатывать исключения.
Синтаксис:
try {
// Код, который может сгенерировать исключение
}
catch (тип_исключения1 параметр1) {
// Обработка первого типа исключения
}
catch (тип_исключения2 параметр2) {
// Обработка второго типа исключения
}
catch (...) {
// Обработка всех остальных исключений
}
Пример многоуровневой обработки: #
#include <iostream>
#include <stdexcept>
#include <string>
void performOperation(int value) {
if (value < 0) {
throw std::invalid_argument("Отрицательное значение");
}
if (value > 1000) {
throw std::runtime_error("Слишком большое значение");
}
}
int main() {
try {
performOperation(-5);
}
catch (const std::invalid_argument& e) {
std::cerr << "Ошибка ввода: " << e.what() << std::endl;
}
catch (const std::runtime_error& e) {
std::cerr << "Ошибка выполнения: " << e.what() << std::endl;
}
catch (...) {
std::cerr << "Неизвестная ошибка" << std::endl;
}
return 0;
}
4. Создание собственных исключений #
Можно создавать пользовательские классы исключений, наследуя их от стандартных или базового класса std::exception
.
#include <stdexcept>
#include <string>
class DatabaseException : public std::runtime_error {
public:
DatabaseException(const std::string& message)
: std::runtime_error(message) {}
};
class ConnectionException : public DatabaseException {
public:
ConnectionException(const std::string& message)
: DatabaseException("Ошибка подключения: " + message) {}
};
void connectToDatabase() {
bool connectionFailed = true;
if (connectionFailed) {
throw ConnectionException("Сервер не отвечает");
}
}
5. Раскрутка стека (Stack Unwinding) #
Раскрутка стека — процесс автоматического освобождения ресурсов и вызова деструкторов при распространении исключения вверх по стеку вызовов.
#include <iostream>
#include <memory>
class ResourceManager {
public:
ResourceManager() { std::cout << "Ресурс создан\n"; }
~ResourceManager() { std::cout << "Ресурс освобождён\n"; }
};
void functionC() {
ResourceManager res;
throw std::runtime_error("Ошибка в функции C");
}
void functionB() {
ResourceManager res;
functionC();
}
void functionA() {
ResourceManager res;
try {
functionB();
}
catch (const std::exception& e) {
std::cerr << "Перехвачено исключение: " << e.what() << std::endl;
}
}
int main() {
functionA();
return 0;
}
Ключевые принципы раскрутки стека: #
- Автоматический вызов деструкторов для локальных объектов
- Освобождение захваченных ресурсов
- Поиск подходящего обработчика исключения
Рекомендации по использованию исключений #
- Используйте исключения для обработки действительно исключительных ситуаций
- Не используйте исключения для контроля штатного выполнения программы
- Перехватывайте конкретные типы исключений
- Используйте умные указатели и RAII для безопасного управления ресурсами
- Документируйте возможные исключения в функциях
Заключение #
Обработка исключений — мощный механизм управления ошибками в C++. Правильное использование исключений делает код более надёжным, читаемым и облегчает диагностику проблем во время выполнения программы.