Menu
Coddy logo textTech

Jerarquías de excepciones personalizadas

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 82 de 104.

Si bien la biblioteca estándar proporciona clases de excepciones útiles como std::runtime_error, las aplicaciones del mundo real a menudo se benefician de crear jerarquías de excepciones personalizadas. Al definir tus propias clases de excepciones que heredan de excepciones estándar, puedes crear tipos de error específicos del dominio que incorporen contexto adicional y permitan un manejo de errores más preciso.

Una clase de excepción personalizada normalmente hereda de std::exception o de una de sus clases derivadas y sobrescribe el método what():

#include <exception>
#include <string>

class DatabaseException : public std::exception {
    std::string message;
public:
    DatabaseException(const std::string& msg) : message(msg) {}
    const char* what() const noexcept override { return message.c_str(); }
};

class ConnectionException : public DatabaseException {
public:
    ConnectionException(const std::string& host) 
        : DatabaseException("Failed to connect to: " + host) {}
};

class QueryException : public DatabaseException {
public:
    QueryException(const std::string& query) 
        : DatabaseException("Invalid query: " + query) {}
};

Esta jerarquía permite que los bloques catch gestionen excepciones en diferentes niveles de especificidad:

try {
    // operaciones de base de datos...
    throw ConnectionException("localhost");
} catch (const ConnectionException& e) {
    std::cout << "Connection issue: " << e.what() << "\n";
} catch (const DatabaseException& e) {
    std::cout << "Database error: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cout << "General error: " << e.what() << "\n";
}

El especificador noexcept en what() es importante: garantiza que el método no lanzará excepciones, lo cual es requerido por la interfaz de la clase base. Ordena tus bloques catch desde los más específicos hasta los más general, ya que C++ utiliza el primer controlador coincidente.

challenge icon

Desafío

Fácil

Construyamos un sistema de procesamiento de archivos con una jerarquía de excepciones personalizada que gestione distintos tipos de errores relacionados con archivos. Crearás excepciones específicas del dominio que hereden de una clase de excepción base, lo que permitirá gestionar los errores con precisión en distintos niveles de especificidad.

Organizarás tu código en tres archivos:

  • FileExceptions.h: Define tu jerarquía de excepciones personalizada para las operaciones con archivos.

    Crea una clase base FileException que herede de std::exception. Debe almacenar un mensaje de error y sobrescribir el método what() con el especificador noexcept.

    Después crea dos clases de excepción derivadas:

    • FileNotFoundException: recibe un nombre de archivo y crea el mensaje: File not found: [filename]
    • PermissionDeniedException: recibe un nombre de archivo y crea el mensaje: Permission denied: [filename]

    Cada clase derivada debe llamar al constructor de su clase principal con el mensaje correspondiente.

  • FileProcessor.h: Define una clase FileProcessor que simule operaciones con archivos y lance tus excepciones personalizadas.

    Tu FileProcessor debe tener un método openFile(const std::string& filename) que simule la apertura de un archivo:

    • Si el nombre de archivo contiene missing, lanza una FileNotFoundException
    • Si el nombre de archivo contiene locked, lanza una PermissionDeniedException
    • De lo contrario, devuelve la cadena Successfully opened: [filename]
  • main.cpp: Lee un único nombre de archivo de la entrada.

    Crea un FileProcessor e intenta abrir el archivo. Usa un bloque try-catch con los controladores ordenados de los más específicos a los más generales:

    1. Captura FileNotFoundException e imprime: File Error: [message]
    2. Captura PermissionDeniedException e imprime: Access Error: [message]
    3. Captura FileException e imprime: General File Error: [message]

    Si no se lanza ninguna excepción, imprime el mensaje de éxito devuelto por openFile().

Por ejemplo, con la entrada report.txt:

Successfully opened: report.txt

Con la entrada missing_data.csv:

File Error: File not found: missing_data.csv

Con la entrada locked_config.ini:

Access Error: Permission denied: locked_config.ini

Este desafío demuestra cómo las jerarquías de excepciones personalizadas permiten gestionar los errores con el nivel de detalle adecuado: capturando excepciones específicas cuando necesitas un manejo preciso, o capturando la excepción base cuando quieres gestionar todos los errores de archivos de manera uniforme.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "FileExceptions.h"
#include "FileProcessor.h"

using namespace std;

int main() {
    string filename;
    cin >> filename;
    
    FileProcessor processor;
    
    // TODO: Usa un bloque try-catch para manejar excepciones
    // Ordena los manejadores de más específico a más general:
    // 1. Catch FileNotFoundException - imprime: "File Error: [message]"
    // 2. Catch PermissionDeniedException - imprime: "Access Error: [message]"
    // 3. Catch FileException - imprime: "General File Error: [message]"
    // Si no hay excepción, imprime el mensaje de éxito de openFile()
    
    try {
        // TODO: Llama a processor.openFile() y maneja el resultado
        
    }
    // TODO: Añade bloques catch en orden de más específico a más general
    
    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

Practica por tu cuenta: Compilador de C++ online