Обработка исключений в ООП
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 81 из 104.
Обработка исключений в C++ предоставляет структурированный способ обнаружения ошибок времени выполнения и реагирования на них. В объектно-ориентированном программировании исключения естественным образом интегрируются с иерархиями классов, позволяя создавать содержательные типы ошибок, содержащие контекст о том, что пошло не так.
Базовый механизм использует три ключевых слова: try, throw и catch. Код, который might завершиться неудачей, помещается в блок try, ошибки обозначаются с помощью throw, а блоки catch обрабатывают определённые типы exception:
#include <iostream>
#include <stdexcept>
class BankAccount {
double balance;
public:
BankAccount(double b) : balance(b) {}
void withdraw(double amount) {
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}
double getBalance() const { return balance; }
};
int main() {
BankAccount account(100.0);
try {
account.withdraw(150.0);
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << "\n";
}
std::cout << "Balance: " << account.getBalance() << "\n";
}Стандартная библиотека C++ предоставляет иерархию классов исключений, корнем которой является std::exception. К распространённым производным классам относятся std::runtime_error для ошибок времени выполнения и std::logic_error для ошибок в программе. Все стандартные exceptions предоставляют what() method, который возвращает сообщение об ошибке.
Когда выбрасывается exception, стек разворачивается — вызываются деструкторы всех локальных объектов, пока программа ищет соответствующий блок catch. Это обеспечивает корректную очистку даже при возникновении ошибок, поэтому RAII и обработка exception так хорошо работают вместе.
Задание
ЛегкоДавайте создадим безопасный калькулятор деления, демонстрирующий обработку исключений в объектно-ориентированном контексте. Вы создадите Calculator class, который выполняет операции деления и выбрасывает понятные исключения, когда что-то идёт не так, а затем корректно обработаете эти исключения в своей основной программе.
Вы организуете свой код в трёх файлах:
Calculator.h: Define вашCalculatorclass, который выполняет математические операции с proper обработкой ошибок.Ваш
Calculatorshould have a methoddivide(double numerator, double denominator), который возвращает результат деления. Однако если denominator равен нулю, он должен выброситьstd::runtime_errorс messageDivision by zero. Если either numerator, либо denominator отрицателен, он должен выброситьstd::invalid_argumentс messageNegative values not allowed.Также include a method
safeSqrt(double value), который возвращает квадратный корень value. Если value отрицателен, выброситеstd::runtime_errorс messageCannot compute square root of negative number. Вам потребуется include<cmath>для функцииsqrt.Calculator.cpp: Implement методы, объявленные в вашем заголовочном файле. Убедитесь, что include необходимые заголовочные файлы (<stdexcept>и<cmath>) и проверьте условия перед выполнением операций.main.cpp: Считайте три inputs (каждый в отдельной строке):- Тип операции: either
divideorsqrt - Первое число (double)
- Второе число (double): используется только для деления, пропускается для sqrt
Create объект
Calculatorи выполните запрошенную операцию внутри try block. Если операция выполнена успешно, выведитеResult: [value]. Если исключение поймано, выведитеError: [message], где message берётся из методаwhat()исключения.After блока try-catch (независимо от того, произошло ли исключение), выведите
Calculation complete, чтобы продемонстрировать, что программа продолжает выполнение после обработки исключений.- Тип операции: either
Например, с inputs divide, 10 и 2:
Result: 5
Calculation completeС inputs divide, 10 и 0:
Error: Division by zero
Calculation completeС inputs divide, -5 и 2:
Error: Negative values not allowed
Calculation completeС inputs sqrt, 16 и 0:
Result: 4
Calculation completeС inputs sqrt, -9 и 0:
Error: Cannot compute square root of negative number
Calculation completeОбратите внимание, как программа корректно обрабатывает ошибки, перехватывая исключения и отображая понятные сообщения, а затем продолжает выполнение. Этот шаблон поддерживает чистую обработку ошибок, а вашу основную логику сосредоточенной на основном успешном сценарии.
Попробуйте сами
#include <iostream>
#include <string>
#include "Calculator.h"
using namespace std;
int main() {
// Читаем входные данные
string operation;
double num1, num2;
cin >> operation;
cin >> num1;
cin >> num2;
// Создаём объект Calculator
Calculator calc;
// TODO: Используйте блок try-catch для выполнения операции
// - Если operation равно "divide", вызовите calc.divide(num1, num2)
// - Если operation равно "sqrt", вызовите calc.safeSqrt(num1)
// - При успехе выведите "Result: " за которым следует результат
// - Перехватывайте исключения и выводите "Error: " за которым следует сообщение исключения (используйте what())
// - After the try-catch, always print "Calculation complete"
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнерПотренируйтесь самостоятельно: Онлайн-компилятор C++