Lección 17 · 17 de 20
Jerarquías de excepciones · C++
Cree sus propias excepciones en C++ derivando de std::runtime_error, con datos extra, y captúrelas de la más concreta a la más general.

Las excepciones propias se crean heredando de las estándar. AppError deriva de std::runtime_error y, con using std::runtime_error::runtime_error;, hereda su constructor, que recibe el mensaje que luego devuelve what(). Todas las excepciones de la aplicación serán AppError y, por tanto, también std::exception.
Las derivadas añaden información útil: ValidationError guarda el campo que está mal (field) y PaymentError, el código que devuelve el banco (code). Así, quien captura el error no tiene que adivinarlo leyendo el mensaje.
checkout valida el pedido y lanza la excepción que corresponde: un correo sin @, un carrito vacío o un importe que el banco rechaza. Si todo va bien, cobra y escribe «OK». El bucle recorre los pedidos con *structured bindings* ([email, amount]) y cada try atiende uno, así que un fallo no detiene a los demás.
Los catch se prueban en orden y se queda el primero que encaja. Por eso van de lo más concreto a lo más general: si catch (const AppError&) fuera el primero, se llevaría también los errores de validación y de pago. Capture siempre por referencia `const`: evita copias y el recorte del objeto, y conserva el tipo real.
Código · main.cpp
#include <iostream>#include <stdexcept>#include <string>#include <utility>#include <vector>// A family of errors: all of them are AppError, and AppError is a runtime_error.struct AppError : std::runtime_error { using std::runtime_error::runtime_error; };struct ValidationError : AppError { std::string field; // extra data about the error ValidationError(const std::string& f, const std::string& msg) : AppError(msg), field(f) {}};struct PaymentError : AppError { int code; explicit PaymentError(int c) : AppError("payment declined"), code(c) {}};void checkout(const std::string& email, double amount) { if (email.find('@') == std::string::npos) throw ValidationError("email", "missing @"); if (amount <= 0) throw AppError("the cart is empty"); if (amount > 1000) throw PaymentError(51); std::cout << "OK: " << amount << " EUR charged to " << email << "\n";}int main() { std::vector<std::pair<std::string, double>> orders = { {"ana@mail.com", 59.9}, {"luis.mail.com", 20}, {"eva@mail.com", 1500}, {"leo@mail.com", 0}}; for (const auto& [email, amount] : orders) { try { checkout(email, amount); } // the catches go from most to least specific catch (const ValidationError& e) { std::cout << "Invalid " << e.field << ": " << e.what() << "\n"; } catch (const PaymentError& e) { std::cout << "Bank code " << e.code << ": " << e.what() << "\n"; } catch (const AppError& e) { std::cout << "Cannot check out: " << e.what() << "\n"; } }}Salida · c++ -std=c++17 main.cpp -o main && ./main
OK: 59.9 EUR charged to ana@mail.com Invalid email: missing @ Bank code 51: payment declined Cannot check out: the cart is empty
Para recordar
- Derive sus excepciones de
std::runtime_error(o de otra estándar). using Base::Base;hereda los constructores de la base.- Las excepciones pueden llevar datos además del mensaje.
- Ordene los
catchde la clase más derivada a la más general. - Capture por
const&, nunca por valor.
Ejercicio
Cree StockError, derivada de AppError, con el nombre del producto agotado. Lánzela en checkout cuando el correo sea ana@mail.com y añada su catch en el lugar correcto.