Введение
В этом лабораторном занятии (Lab) вы научитесь основным концепциям обработки исключений в C++. Вы начнете с понимания того, как бросать (throw) и перехватывать (catch) базовые исключения, которые представляют собой способ обработки ошибок времени выполнения или непредвиденных ситуаций в вашей программе. Затем вы изучите использование ключевых слов try, catch и throw, а также стандартных классов исключений, таких как std::exception. Кроме того, вы научитесь определять пользовательские классы исключений и реализовывать несколько блоков catch для различных типов исключений. Наконец, вы изучите использование вложенных блоков try-catch для обработки исключений на разных уровнях вашей программы.
Бросание и перехват базовых исключений
На этом этапе вы научитесь основным концепциям обработки исключений в C++, сосредоточившись на том, как бросать (throw) и перехватывать (catch) базовые исключения. Исключения - это способ обработки ошибок времени выполнения или непредвиденных ситуаций в вашей программе.
Откройте WebIDE и создайте новый файл с именем basic_exceptions.cpp в директории ~/project:
touch ~/project/basic_exceptions.cpp
Добавьте следующий код в файл basic_exceptions.cpp:
#include <iostream>
#include <stdexcept>
int divide(int numerator, int denominator) {
// Throw an exception if denominator is zero
if (denominator == 0) {
throw std::runtime_error("Division by zero is not allowed!");
}
return numerator / denominator;
}
int main() {
try {
// Attempt a normal division
int result1 = divide(10, 2);
std::cout << "10 / 2 = " << result1 << std::endl;
// Attempt division by zero
int result2 = divide(10, 0);
std::cout << "This line will not be executed" << std::endl;
}
catch (const std::exception& e) {
// Catch and handle the exception
std::cout << "Error: " << e.what() << std::endl;
}
return 0;
}
Разберем основные компоненты:
Ключевое слово
throw:- Используется для генерации исключения при возникновении ошибки
- В этом примере мы бросаем исключение
std::runtime_error, когда пытаемся выполнить деление на ноль
Блок
try:- Содержит код, который может сгенерировать исключение
- Позволяет выполнять рискованные операции
- Если возникает исключение, управление программой передается в блок
catch
Блок
catch:- Обрабатывает исключение, брошенное в блоке
try - Перехватывает исключения определенного типа (в данном случае
std::exception) - Использует
e.what()для получения сообщения об ошибке
- Обрабатывает исключение, брошенное в блоке
Скомпилируйте и запустите программу:
g++ basic_exceptions.cpp -o basic_exceptions
./basic_exceptions
Пример вывода:
10 / 2 = 5
Error: Division by zero is not allowed!
Основные моменты по базовой обработке исключений:
- Исключения предоставляют способ элегантно обрабатывать ошибки времени выполнения
throwгенерирует исключениеtryиcatchработают вместе для управления исключительными ситуациями- Предотвращает аварийное завершение программы, обеспечивая контролируемую обработку ошибок
Понимание ключевых слов try, catch и throw
На этом этапе вы более детально познакомитесь с тремя ключевыми словами обработки исключений в C++: try, catch и throw. Эти ключевые слова работают вместе, чтобы создать надежный механизм обработки ошибок в ваших программах.
Откройте WebIDE и создайте новый файл с именем exception_keywords.cpp в директории ~/project:
touch ~/project/exception_keywords.cpp
Добавьте следующий код в файл exception_keywords.cpp:
#include <iostream>
#include <string>
// Function that might throw an exception
int processAge(int age) {
// Throw an exception for invalid age
if (age < 0) {
throw std::string("Age cannot be negative");
}
if (age > 120) {
throw std::string("Age is unrealistically high");
}
return age;
}
int main() {
// First try block: handling age validation
try {
// Successful case
int validAge = processAge(25);
std::cout << "Valid age: " << validAge << std::endl;
// This will throw an exception
int invalidAge1 = processAge(-5);
std::cout << "This line will not be executed" << std::endl;
}
catch (const std::string& errorMessage) {
// Catch block to handle the thrown exception
std::cout << "Error: " << errorMessage << std::endl;
}
// Second try block: another example
try {
// This will throw another exception
int invalidAge2 = processAge(150);
std::cout << "This line will also not be executed" << std::endl;
}
catch (const std::string& errorMessage) {
std::cout << "Error: " << errorMessage << std::endl;
}
return 0;
}
Разберем основные компоненты:
Ключевое слово
throw:- Используется для генерации исключения, когда выполняется определенное условие
- Может бросать различные типы объектов (строки, целые числа, пользовательские объекты)
- Незамедлительно останавливает выполнение текущей функции
Блок
try:- Содержит код, который может сгенерировать исключение
- Позволяет выполнять рискованные операции
- Если возникает исключение, управление программой передается в соответствующий блок
catch
Блок
catch:- Перехватывает и обрабатывает определенные типы исключений
- Может быть несколько блоков
catchдля различных типов исключений - Предотвращает аварийное завершение программы, элегантно обрабатывая ошибки
Скомпилируйте и запустите программу:
g++ exception_keywords.cpp -o exception_keywords
./exception_keywords
Пример вывода:
Valid age: 25
Error: Age cannot be negative
Error: Age is unrealistically high
Основные моменты о ключевых словах обработки исключений:
throwсигнализирует о возникновении ошибкиtryопределяет блок кода, который может сгенерировать исключениеcatchобрабатывает исключение и предотвращает завершение программы- Исключения предоставляют структурированный способ управления ошибками времени выполнения
Использование стандартных классов исключений (std::exception)
На этом этапе вы узнаете о стандартных классах исключений в C++ и о том, как использовать иерархию std::exception для обработки различных типов ошибок времени выполнения. Стандартная библиотека C++ предоставляет набор предопределенных классов исключений, которые охватывают различные сценарии ошибок.
Откройте WebIDE и создайте новый файл с именем standard_exceptions.cpp в директории ~/project:
touch ~/project/standard_exceptions.cpp
Добавьте следующий код в файл standard_exceptions.cpp:
#include <iostream>
#include <stdexcept>
#include <limits>
double divideNumbers(double numerator, double denominator) {
// Check for division by zero using std::runtime_error
if (denominator == 0) {
throw std::runtime_error("Division by zero is not allowed!");
}
return numerator / denominator;
}
void checkArrayIndex(int* arr, int size, int index) {
// Check for out-of-range access using std::out_of_range
if (index < 0 || index >= size) {
throw std::out_of_range("Array index is out of bounds!");
}
std::cout << "Value at index " << index << ": " << arr[index] << std::endl;
}
int main() {
try {
// Demonstrate division by zero exception
std::cout << "Attempting division:" << std::endl;
double result = divideNumbers(10, 0);
}
catch (const std::runtime_error& e) {
std::cout << "Runtime Error: " << e.what() << std::endl;
}
try {
// Demonstrate array index out of range exception
int numbers[] = {1, 2, 3, 4, 5};
int arraySize = 5;
std::cout << "\nAccessing array elements:" << std::endl;
checkArrayIndex(numbers, arraySize, 2); // Valid index
checkArrayIndex(numbers, arraySize, 10); // Invalid index
}
catch (const std::out_of_range& e) {
std::cout << "Out of Range Error: " << e.what() << std::endl;
}
return 0;
}
Рассмотрим стандартные классы исключений:
std::exception:- Базовый класс для всех стандартных исключений
- Предоставляет виртуальный метод
what(), чтобы получить описание ошибки
Общие производные классы исключений:
std::runtime_error: Для ошибок времени выполнения, которые могут быть обнаружены только во время выполнения программыstd::out_of_range: Когда индекс или итератор находятся вне допустимого диапазона- Другие общие классы включают
std::logic_error,std::invalid_argumentи т.д.
Скомпилируйте и запустите программу:
g++ standard_exceptions.cpp -o standard_exceptions
./standard_exceptions
Пример вывода:
Attempting division:
Runtime Error: Division by zero is not allowed!
Accessing array elements:
Value at index 2: 3
Out of Range Error: Array index is out of bounds!
Основные моменты о стандартных классах исключений:
- Предоставляют структурированный способ обработки различных типов ошибок
- Каждый класс исключений служит определенной цели
- Метод
what()возвращает описательное сообщение об ошибке - Помогает создавать более надежную и информативную обработку ошибок
Определение пользовательских классов исключений
На этом этапе вы узнаете, как создавать свои собственные пользовательские классы исключений в C++. Пользовательские исключения позволяют определить конкретные типы ошибок, адаптированные к уникальным требованиям вашего приложения.
Откройте WebIDE и создайте новый файл с именем custom_exceptions.cpp в директории ~/project:
touch ~/project/custom_exceptions.cpp
Добавьте следующий код в файл custom_exceptions.cpp:
#include <iostream>
#include <string>
#include <stdexcept>
// Custom exception class for bank account errors
class InsufficientFundsException : public std::runtime_error {
public:
// Constructor that takes account balance and withdrawal amount
InsufficientFundsException(double balance, double amount)
: std::runtime_error("Insufficient funds"),
currentBalance(balance),
withdrawalAmount(amount) {}
// Method to get detailed error information
double getCurrentBalance() const { return currentBalance; }
double getWithdrawalAmount() const { return withdrawalAmount; }
private:
double currentBalance;
double withdrawalAmount;
};
class BankAccount {
private:
double balance;
public:
BankAccount(double initialBalance) : balance(initialBalance) {}
void withdraw(double amount) {
// Check if withdrawal amount exceeds current balance
if (amount > balance) {
throw InsufficientFundsException(balance, amount);
}
balance -= amount;
std::cout << "Withdrawal successful. Remaining balance: $"
<< balance << std::endl;
}
double getBalance() const { return balance; }
};
int main() {
try {
// Create a bank account with initial balance
BankAccount account(100.0);
// Attempt a valid withdrawal
account.withdraw(50.0);
// Attempt an invalid withdrawal
account.withdraw(75.0);
}
catch (const InsufficientFundsException& e) {
std::cout << "Error: " << e.what() << std::endl;
std::cout << "Current Balance: $" << e.getCurrentBalance() << std::endl;
std::cout << "Attempted Withdrawal: $" << e.getWithdrawalAmount() << std::endl;
}
return 0;
}
Разберем пользовательский класс исключений:
Наследование от
std::runtime_error:- Предоставляет базу для пользовательских классов исключений
- Позволяет использовать метод
what()для получения описания ошибки
Особенности пользовательского исключения:
- Конструктор с дополнительным контекстом ошибки
- Методы для получения конкретных деталей ошибки
- Предоставляет более информативную обработку ошибок
Скомпилируйте и запустите программу:
g++ custom_exceptions.cpp -o custom_exceptions
./custom_exceptions
Пример вывода:
Withdrawal successful. Remaining balance: $50
Error: Insufficient funds
Current Balance: $50
Attempted Withdrawal: $75
Основные моменты о пользовательских классах исключений:
- Наследуются от стандартных классов исключений
- Добавляют конкретный контекст ошибки и методы
- Предоставляют более детальную информацию об ошибке
- Улучшают обработку ошибок и отладку
Реализация нескольких блоков catch для различных типов исключений
На этом этапе вы узнаете, как обрабатывать несколько типов исключений с помощью разных блоков catch. Этот подход позволяет вам обеспечить конкретную обработку ошибок для различных типов исключений, которые могут возникнуть в вашей программе.
Откройте WebIDE и создайте новый файл с именем multiple_catch.cpp в директории ~/project:
touch ~/project/multiple_catch.cpp
Добавьте следующий код в файл multiple_catch.cpp:
#include <iostream>
#include <stdexcept>
#include <string>
class InvalidAgeException : public std::runtime_error {
public:
InvalidAgeException(int age)
: std::runtime_error("Invalid age"), invalidAge(age) {}
int getInvalidAge() const { return invalidAge; }
private:
int invalidAge;
};
class Student {
private:
std::string name;
int age;
public:
void setStudent(const std::string& studentName, int studentAge) {
// Validate name length
if (studentName.length() < 2) {
throw std::length_error("Name is too short");
}
// Validate age range
if (studentAge < 0 || studentAge > 120) {
throw InvalidAgeException(studentAge);
}
name = studentName;
age = studentAge;
}
void displayInfo() const {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
};
int main() {
Student student;
// First attempt: Short name
try {
student.setStudent("A", 25);
}
catch (const std::length_error& e) {
std::cout << "Length Error: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Age Error: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
// Second attempt: Invalid age
try {
student.setStudent("John Doe", 150);
}
catch (const std::length_error& e) {
std::cout << "Length Error: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Age Error: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
// Successful attempt
try {
student.setStudent("Alice", 20);
student.displayInfo();
}
catch (const std::length_error& e) {
std::cout << "Length Error: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Age Error: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
return 0;
}
Разберем несколько блоков catch:
Несколько блоков
catch:- Позволяют обрабатывать различные типы исключений
- Выполняются в порядке от наиболее конкретного к наиболее общему
- Каждый блок может обрабатывать определенный тип исключения
Стратегия обработки исключений:
- Первый блок
catchобрабатываетstd::length_error - Второй блок
catchобрабатываетInvalidAgeException - Предоставляет конкретные сообщения об ошибке для разных сценариев
- Первый блок
Скомпилируйте и запустите программу:
g++ multiple_catch.cpp -o multiple_catch
./multiple_catch
Пример вывода:
Length Error: Name is too short
Age Error: Invalid age (150)
Name: Alice, Age: 20
Основные моменты о нескольких блоках catch:
- Обрабатывают разные типы исключений отдельно
- Предоставляют конкретную обработку ошибок для каждого исключения
- Порядок имеет значение при перехвате исключений
- Позволяет более детально управлять ошибками
Использование вложенных блоков try-catch
На этом этапе вы узнаете, как использовать вложенные блоки try-catch для обработки сложных сценариев ошибок и обеспечения более детальной обработки исключений. Вложенные блоки try-catch позволяют обрабатывать исключения на разных уровнях вашего кода.
Откройте WebIDE и создайте новый файл с именем nested_exceptions.cpp в директории ~/project:
touch ~/project/nested_exceptions.cpp
Добавьте следующий код в файл nested_exceptions.cpp:
#include <iostream>
#include <stdexcept>
#include <string>
class FileReadError : public std::runtime_error {
public:
FileReadError(const std::string& filename)
: std::runtime_error("Error reading file"), fileName(filename) {}
std::string getFileName() const { return fileName; }
private:
std::string fileName;
};
class DataProcessor {
public:
void processFile(const std::string& filename) {
try {
// Simulate file reading
if (filename.empty()) {
throw FileReadError("Empty filename");
}
std::cout << "Reading file: " << filename << std::endl;
try {
// Simulate data processing
validateData(filename);
}
catch (const std::runtime_error& e) {
std::cout << "Inner try-catch: Data validation error" << std::endl;
std::cout << "Error details: " << e.what() << std::endl;
// Rethrow the exception to outer catch block
throw;
}
}
catch (const FileReadError& e) {
std::cout << "Outer try-catch: File read error" << std::endl;
std::cout << "Filename: " << e.getFileName() << std::endl;
}
catch (...) {
std::cout << "Caught unknown exception" << std::endl;
}
}
private:
void validateData(const std::string& filename) {
// Simulate data validation
if (filename == "corrupt.txt") {
throw std::runtime_error("Corrupt file data");
}
std::cout << "Data validation successful" << std::endl;
}
};
int main() {
DataProcessor processor;
// Scenario 1: Empty filename
std::cout << "Scenario 1: Empty Filename" << std::endl;
processor.processFile("");
// Scenario 2: Corrupt file
std::cout << "\nScenario 2: Corrupt File" << std::endl;
processor.processFile("corrupt.txt");
// Scenario 3: Valid file
std::cout << "\nScenario 3: Valid File" << std::endl;
processor.processFile("data.txt");
return 0;
}
Разберем вложенные блоки try-catch:
Внешний блок
try-catch:- Обрабатывает исключения на уровне файла
- Перехватывает
FileReadErrorи другие потенциальные ошибки
Внутренний блок
try-catch:- Обрабатывает исключения, специфичные для обработки данных
- Может повторно бросать исключения в внешний блок
catch
Универсальный обработчик (
catch (...)):- Перехватывает любые неожиданные исключения
- Предоставляет последний уровень обработки ошибок
Скомпилируйте и запустите программу:
g++ nested_exceptions.cpp -o nested_exceptions
./nested_exceptions
Пример вывода:
Scenario 1: Empty Filename
Outer try-catch: File read error
Filename: Empty filename
Scenario 2: Corrupt File
Reading file: corrupt.txt
Inner try-catch: Data validation error
Error details: Corrupt file data
Scenario 3: Valid File
Reading file: data.txt
Data validation successful
Основные моменты о вложенных блоках try-catch:
- Предоставляют несколько уровней обработки исключений
- Позволяют более детально управлять ошибками
- Можно повторно бросать исключения в внешние блоки
catch - Полезны для сложных сценариев ошибок
Резюме
В этом практическом занятии вы изучили основные концепции обработки исключений в C++. Вы начали с понимания того, как бросать и перехватывать базовые исключения с использованием ключевых слов throw, try и catch. Вы рассмотрели применение класса исключений std::runtime_error для обработки ошибок деления на ноль. Кроме того, вы узнали, как получить доступ к сообщению об ошибке с помощью функции e.what() внутри блока catch. Эти техники позволяют элегантно обрабатывать ошибки времени выполнения и предотвращать аварийное завершение программы, что позволяет вам создавать более надежные и устойчивые приложения на C++.



