Introducción
En este laboratorio, aprenderás los conceptos fundamentales del manejo de excepciones en C++. Comenzarás por entender cómo lanzar y capturar excepciones básicas, que son una forma de manejar errores en tiempo de ejecución o situaciones inesperadas en tu programa. Luego, explorarás el uso de las palabras clave try, catch y throw, así como de las clases de excepciones estándar como std::exception. Además, aprenderás cómo definir clases de excepciones personalizadas e implementar múltiples bloques catch para diferentes tipos de excepciones. Finalmente, explorarás el uso de bloques try-catch anidados para manejar excepciones en diferentes niveles de tu programa.
Lanzar y capturar excepciones básicas
En este paso, aprenderás los conceptos fundamentales del manejo de excepciones en C++, centrándote en cómo lanzar y capturar excepciones básicas. Las excepciones son una forma de manejar errores en tiempo de ejecución o situaciones inesperadas en tu programa.
Abre el WebIDE y crea un nuevo archivo llamado basic_exceptions.cpp en el directorio ~/project:
touch ~/project/basic_exceptions.cpp
Agrega el siguiente código a 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;
}
Analicemos los componentes clave:
Palabra clave
throw:- Se utiliza para generar una excepción cuando ocurre un error
- En este ejemplo, lanzamos una
std::runtime_errorcuando se intenta una división por cero
Bloque
try:- Contiene código que puede generar una excepción
- Te permite intentar operaciones riesgosas
- Si ocurre una excepción, el control del programa se mueve al bloque
catch
Bloque
catch:- Maneja la excepción lanzada en el bloque
try - Captura excepciones de un tipo específico (en este caso,
std::exception) - Utiliza
e.what()para obtener el mensaje de error
- Maneja la excepción lanzada en el bloque
Compila y ejecuta el programa:
g++ basic_exceptions.cpp -o basic_exceptions
./basic_exceptions
Ejemplo de salida:
10 / 2 = 5
Error: Division by zero is not allowed!
Puntos clave sobre el manejo básico de excepciones:
- Las excepciones proporcionan una forma de manejar errores en tiempo de ejecución de manera elegante
throwgenera una excepcióntryycatchtrabajan juntos para manejar situaciones excepcionales- Evita que el programa se bloquee al proporcionar un manejo controlado de errores
Comprender las palabras clave try, catch y throw
En este paso, profundizarás en las tres palabras clave fundamentales del manejo de excepciones en C++: try, catch y throw. Estas palabras clave trabajan juntas para crear un sólido mecanismo de manejo de errores en tus programas.
Abre el WebIDE y crea un nuevo archivo llamado exception_keywords.cpp en el directorio ~/project:
touch ~/project/exception_keywords.cpp
Agrega el siguiente código a 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;
}
Analicemos los componentes clave:
Palabra clave
throw:- Se utiliza para generar una excepción cuando se cumple una condición específica
- Puede lanzar diferentes tipos de objetos (cadenas, enteros, objetos personalizados)
- Detiene inmediatamente la ejecución de la función actual
Bloque
try:- Contiene código que puede generar una excepción
- Te permite intentar operaciones riesgosas
- Si ocurre una excepción, el control del programa se mueve al bloque
catchcoincidente
Bloque
catch:- Captura y maneja tipos específicos de excepciones
- Puede haber múltiples bloques
catchpara diferentes tipos de excepciones - Evita que el programa se bloquee manejando los errores de manera elegante
Compila y ejecuta el programa:
g++ exception_keywords.cpp -o exception_keywords
./exception_keywords
Ejemplo de salida:
Valid age: 25
Error: Age cannot be negative
Error: Age is unrealistically high
Puntos clave sobre las palabras clave de excepciones:
throwseñala una condición de errortrydefine un bloque de código que puede generar una excepcióncatchmaneja la excepción y evita la terminación del programa- Las excepciones proporcionan una forma estructurada de manejar errores en tiempo de ejecución
Utilizar clases de excepciones estándar (std::exception)
En este paso, aprenderás sobre las clases de excepciones estándar en C++ y cómo utilizar la jerarquía de std::exception para manejar diferentes tipos de errores en tiempo de ejecución. La Biblioteca Estándar de C++ proporciona un conjunto de clases de excepciones predefinidas que cubren diversos escenarios de error.
Abre el WebIDE y crea un nuevo archivo llamado standard_exceptions.cpp en el directorio ~/project:
touch ~/project/standard_exceptions.cpp
Agrega el siguiente código a 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;
}
Exploremos las clases de excepciones estándar:
std::exception:- Clase base para todas las excepciones estándar
- Proporciona un método virtual
what()para obtener la descripción del error
Clases de excepciones derivadas comunes:
std::runtime_error: Para errores en tiempo de ejecución que solo se pueden detectar durante la ejecución del programastd::out_of_range: Cuando un índice o iterador está fuera del rango válido- Otras clases comunes incluyen
std::logic_error,std::invalid_argument, etc.
Compila y ejecuta el programa:
g++ standard_exceptions.cpp -o standard_exceptions
./standard_exceptions
Ejemplo de salida:
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!
Puntos clave sobre las clases de excepciones estándar:
- Proporcionan una forma estructurada de manejar diferentes tipos de errores
- Cada clase de excepción tiene un propósito específico
- El método
what()devuelve un mensaje de error descriptivo - Ayudan a crear un manejo de errores más robusto e informativo
Definir clases de excepciones personalizadas
En este paso, aprenderás cómo crear tus propias clases de excepciones personalizadas en C++. Las excepciones personalizadas te permiten definir tipos de error específicos adaptados a las necesidades únicas de tu aplicación.
Abre el WebIDE y crea un nuevo archivo llamado custom_exceptions.cpp en el directorio ~/project:
touch ~/project/custom_exceptions.cpp
Agrega el siguiente código a 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;
}
Analicemos la clase de excepción personalizada:
Herencia de
std::runtime_error:- Proporciona una base para las clases de excepciones personalizadas
- Permite utilizar el método
what()para obtener la descripción del error
Características de la excepción personalizada:
- Constructor con contexto de error adicional
- Métodos para recuperar detalles específicos del error
- Proporciona un manejo de errores más informativo
Compila y ejecuta el programa:
g++ custom_exceptions.cpp -o custom_exceptions
./custom_exceptions
Ejemplo de salida:
Withdrawal successful. Remaining balance: $50
Error: Insufficient funds
Current Balance: $50
Attempted Withdrawal: $75
Puntos clave sobre las clases de excepciones personalizadas:
- Heredan de las clases de excepciones estándar
- Añaden contexto de error específico y métodos
- Proporcionan información de error más detallada
- Mejoran el manejo de errores y la depuración
Implementar múltiples bloques catch para diferentes tipos de excepciones
En este paso, aprenderás cómo manejar múltiples tipos de excepciones utilizando diferentes bloques catch. Este enfoque te permite proporcionar un manejo de errores específico para varios tipos de excepciones que pueden ocurrir en tu programa.
Abre el WebIDE y crea un nuevo archivo llamado multiple_catch.cpp en el directorio ~/project:
touch ~/project/multiple_catch.cpp
Agrega el siguiente código a 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;
}
Analicemos los múltiples bloques catch:
Múltiples bloques
catch:- Permiten manejar diferentes tipos de excepciones
- Se ejecutan en orden desde el más específico al más general
- Cada bloque puede manejar un tipo de excepción específico
Estrategia de manejo de excepciones:
- El primer bloque
catchmanejastd::length_error - El segundo bloque
catchmanejaInvalidAgeException - Proporciona mensajes de error específicos para diferentes escenarios
- El primer bloque
Compila y ejecuta el programa:
g++ multiple_catch.cpp -o multiple_catch
./multiple_catch
Ejemplo de salida:
Length Error: Name is too short
Age Error: Invalid age (150)
Name: Alice, Age: 20
Puntos clave sobre los múltiples bloques catch:
- Manejan diferentes tipos de excepciones por separado
- Proporcionan un manejo de errores específico para cada excepción
- El orden es importante al capturar excepciones
- Permiten un manejo de errores más detallado
Utilizar bloques try-catch anidados
En este paso, aprenderás cómo utilizar bloques try-catch anidados para manejar escenarios de error complejos y proporcionar un manejo de excepciones más detallado. Los bloques try-catch anidados te permiten manejar excepciones en diferentes niveles de tu código.
Abre el WebIDE y crea un nuevo archivo llamado nested_exceptions.cpp en el directorio ~/project:
touch ~/project/nested_exceptions.cpp
Agrega el siguiente código a 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;
}
Analicemos los bloques try-catch anidados:
Bloque
try-catchexterior:- Maneja excepciones a nivel de archivo
- Captura
FileReadErrory otros posibles errores
Bloque
try-catchinterior:- Maneja excepciones específicas del procesamiento de datos
- Puede volver a lanzar excepciones al bloque
catchexterior
Manejador general (
catch (...)):- Captura cualquier excepción inesperada
- Proporciona una capa final de manejo de errores
Compila y ejecuta el programa:
g++ nested_exceptions.cpp -o nested_exceptions
./nested_exceptions
Ejemplo de salida:
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
Puntos clave sobre los bloques try-catch anidados:
- Proporcionan múltiples niveles de manejo de excepciones
- Permiten un manejo de errores más detallado
- Pueden volver a lanzar excepciones a bloques
catchexteriores - Son útiles para escenarios de error complejos
Resumen
En este laboratorio, aprendiste los conceptos fundamentales del manejo de excepciones en C++. Comenzaste por entender cómo lanzar y capturar excepciones básicas utilizando las palabras clave throw, try y catch. Exploraste el uso de la clase de excepción std::runtime_error para manejar errores de división por cero. Además, aprendiste cómo acceder al mensaje de error utilizando la función e.what() dentro del bloque catch. Estas técnicas proporcionan una forma de manejar los errores en tiempo de ejecución de manera elegante y evitar que el programa se bloquee, lo que te permite escribir aplicaciones de C++ más robustas y confiables.



