Menu
Coddy logo textTech

Manejo de excepciones en POO

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 81 de 104.

El manejo de excepciones en C++ proporciona una forma estructurada de detectar y responder a errores en tiempo de ejecución. En la programación orientada a objetos, las excepciones se integran de forma natural con las jerarquías de clases, lo que permite crear tipos de error significativos que contienen información contextual sobre lo que salió mal.

El mecanismo básico utiliza tres palabras clave: try, throw y catch. El código que podría fallar se coloca en un bloque try, los errores se señalizan con throw y los bloques catch manejan tipos de excepciones específicos:

#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";
}

La biblioteca estándar de C++ proporciona una jerarquía de clases de excepción con raíz en std::exception. Las clases derivadas comunes incluyen std::runtime_error para problemas de tiempo de ejecución y std::logic_error para errores de programación. Todas las excepciones estándar proporcionan un método what() que devuelve un mensaje de error.

Cuando se lanza una excepción, la pila se desenrolla: se llama a los destructores de todos los objetos locales mientras el programa busca un bloque catch coincidente. Esto garantiza una limpieza adecuada incluso cuando ocurren errores, razón por la cual RAII y el manejo de excepciones funcionan tan bien juntos.

challenge icon

Desafío

Fácil

Vamos a construir una calculadora de división segura que demuestre el manejo de excepciones en un contexto orientado a objetos. Crearás una clase Calculator que realice operaciones de división y lance excepciones significativas cuando algo salga mal, para luego manejar esas excepciones de manera adecuada en tu programa principal.

Organizarás tu código en tres archivos:

  • Calculator.h: Define tu clase Calculator que realiza operaciones matemáticas con un manejo de errores adecuado.

    Tu Calculator debe tener un método divide(double numerator, double denominator) que devuelva el resultado de la división. Sin embargo, si el denominador es cero, debe lanzar un std::runtime_error con el mensaje "Division by zero". Si el numerador o el denominador son negativos, debe lanzar un std::invalid_argument con el mensaje "Negative values not allowed".

    También incluye un método safeSqrt(double value) que devuelva la raíz cuadrada del valor. Si el valor es negativo, lanza un std::runtime_error con el mensaje "Cannot compute square root of negative number". Necesitarás incluir <cmath> para la función sqrt.

  • Calculator.cpp: Implementa los métodos declarados en tu archivo de cabecera. Asegúrate de incluir los encabezados necesarios (<stdexcept> y <cmath>) y verifica las condiciones antes de realizar las operaciones.
  • main.cpp: Lee tres entradas (cada una en una línea separada):
    1. Tipo de operación: ya sea "divide" o "sqrt"
    2. Primer número (double)
    3. Segundo número (double) — solo se usa para la división, se ignora para sqrt

    Crea un objeto Calculator y realiza la operación solicitada dentro de un bloque try. Si la operación tiene éxito, imprime "Result: " seguido del valor. Si se captura una excepción, imprime "Error: " seguido del mensaje que proviene del método what() de la excepción.

    Después del bloque try-catch (independientemente de si ocurrió una excepción), imprime "Calculation complete" para demostrar que el programa continúa ejecutándose después de manejar las excepciones.

Por ejemplo, con las entradas "divide", 10 y 2:

Result: 5
Calculation complete

Con las entradas "divide", 10 y 0:

Error: Division by zero
Calculation complete

Con las entradas "divide", -5 y 2:

Error: Negative values not allowed
Calculation complete

Con las entradas "sqrt", 16 y 0:

Result: 4
Calculation complete

Con las entradas "sqrt", -9 y 0:

Error: Cannot compute square root of negative number
Calculation complete

Observa cómo el programa maneja los errores con elegancia al capturar excepciones y mostrar mensajes significativos, para luego continuar con la ejecución. Este patrón mantiene limpio el manejo de errores y permite que la lógica principal se centre en el flujo normal de ejecución.

Hoja de referencia

El manejo de excepciones en C++ utiliza tres palabras clave: try, throw y catch. El código que podría fallar se coloca en un bloque try, los errores se señalan con throw y los bloques catch manejan tipos de excepciones específicos:

try {
    // Código que podría lanzar una excepción
    account.withdraw(150.0);
} catch (const std::runtime_error& e) {
    std::cout << "Error: " << e.what() << "\n";
}

Lanzar excepciones desde métodos de clase:

void withdraw(double amount) {
    if (amount > balance) {
        throw std::runtime_error("Insufficient funds");
    }
    balance -= amount;
}

La biblioteca estándar de C++ proporciona clases de excepción con raíz en std::exception. Los tipos comunes incluyen:

  • std::runtime_error - para problemas en tiempo de ejecución
  • std::logic_error - para errores de programación
  • std::invalid_argument - para argumentos no válidos

Todas las excepciones estándar proporcionan un método what() que devuelve un mensaje de error.

Cuando se lanza una excepción, la pila se desenrolla (stack unwinding): se llama a los destructores de todos los objetos locales mientras el programa busca un bloque catch coincidente, lo que garantiza una limpieza adecuada.

Incluya <stdexcept> para usar las clases de excepción estándar.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "Calculator.h"

using namespace std;

int main() {
    // Leer las entradas
    string operation;
    double num1, num2;
    
    cin >> operation;
    cin >> num1;
    cin >> num2;
    
    // Crear el objeto Calculator
    Calculator calc;
    
    // TODO: Usar un bloque try-catch para realizar la operación
    // - Si operation es "divide", llamar a calc.divide(num1, num2)
    // - Si operation es "sqrt", llamar a calc.safeSqrt(num1)
    // - En caso de éxito, imprimir "Result: " seguido del resultado
    // - Capturar las excepciones e imprimir "Error: " seguido del mensaje de la excepción (usar what())
    // - Después del try-catch, imprimir siempre "Calculation complete"
    
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos