Médio C++
Hierarquia de exceções personalizada
Enunciado
Crie uma hierarquia de exceções para uma conta bancária: SaldoInsuficiente herda de std::runtime_error e ValorInvalido herda de std::logic_error. Implemente sacar(double valor) que lança ValorInvalido se valor <= 0 e SaldoInsuficiente se valor > saldo. No main, teste os dois casos e capture cada exceção separadamente, imprimindo mensagens distintas.
Requisitos
SaldoInsuficientedeve herdar destd::runtime_erroreValorInvalidodestd::logic_error.- A função
sacardeve lançar a exceção correta para cada situação. - O
maindeve tercatchseparados para cada tipo, imprimindo"Erro de saldo: ..."e"Erro de valor: ...".
Código inicial
#include <iostream>
#include <stdexcept>
// TODO: defina as classes de exceção
class Conta {
double saldo_;
public:
Conta(double saldo) : saldo_(saldo) {}
void sacar(double valor) {
// TODO: lance exceções conforme as regras
}
double saldo() const { return saldo_; }
};
int main() {
// TODO: teste os casos
return 0;
}
Saída esperada
Saldo inicial: 100
Saque de 150: Erro de saldo: saldo insuficiente
Saque de -10: Erro de valor: valor invalido
Saldo final: 100
Ver dica
Defina as classes herdando e passando a mensagem para o construtor da base: class SaldoInsuficiente : public std::runtime_error { public: SaldoInsuficiente() : std::runtime_error("saldo insuficiente") {} };.
Mostrar solução
#include <iostream>
#include <stdexcept>
// Exceção para saldo insuficiente (erro de execução)
class SaldoInsuficiente : public std::runtime_error {
public:
SaldoInsuficiente() : std::runtime_error("saldo insuficiente") {}
};
// Exceção para valor inválido (erro de lógica)
class ValorInvalido : public std::logic_error {
public:
ValorInvalido() : std::logic_error("valor invalido") {}
};
class Conta {
double saldo_;
public:
Conta(double saldo) : saldo_(saldo) {}
void sacar(double valor) {
if (valor <= 0.0)
throw ValorInvalido{}; // erro de programação
if (valor > saldo_)
throw SaldoInsuficiente{}; // falha de execução
saldo_ -= valor;
}
double saldo() const { return saldo_; }
};
int main() {
Conta c(100.0);
std::cout << "Saldo inicial: " << c.saldo() << '\n';
try {
c.sacar(150.0);
} catch (const SaldoInsuficiente& e) {
std::cout << "Saque de 150: Erro de saldo: " << e.what() << '\n';
}
try {
c.sacar(-10.0);
} catch (const ValorInvalido& e) {
std::cout << "Saque de -10: Erro de valor: " << e.what() << '\n';
}
std::cout << "Saldo final: " << c.saldo() << '\n';
return 0;
}
Passo a passo
- Definimos
SaldoInsuficienteherdando destd::runtime_errore passando a mensagem para o construtor da base. - Definimos
ValorInvalidoherdando destd::logic_error, também com mensagem fixa. - A classe
Contaarmazena o saldo e expõesacar. - Em
sacar, verificamos primeiro se o valor é inválido (<= 0) e lançamosValorInvalido. - Depois verificamos se o valor excede o saldo e lançamos
SaldoInsuficiente. - No
main, cada chamada problemática é envolvida em umtrycomcatchespecífico. - O
catchdeSaldoInsuficienteimprime a mensagem de saldo; o deValorInvalido, a de valor. - O saldo permanece 100 porque as exceções impedem a subtração.
Por que funciona
A hierarquia permite capturar exceções específicas ou genéricas. Como ambas herdam de std::exception, um catch (const std::exception&) também as capturaria. A ordem dos catch importa: os mais derivados devem vir antes dos mais genéricos. Aqui usamos catch separados, então a ordem não é problema.
Erros comuns
- Não passar a mensagem para a base:
class SaldoInsuficiente : public std::runtime_error { public: SaldoInsuficiente() {} };deixawhat()vazio. Corrija chamandostd::runtime_error("mensagem"). - Capturar por valor:
catch (SaldoInsuficiente e)causa slicing e perde o tipo derivado. Use referência. - Ordem errada dos
catch: se capturarstd::exceptionantes deSaldoInsuficiente, o específico nunca é alcançado. - Lançar após modificar o estado: se subtrair o saldo antes de verificar, o saldo fica inconsistente. Verifique antes de alterar.
Outra forma de resolver
Poderia usar uma única exceção base ContaErro e derivar as duas, capturando ContaErro e inspecionando um código. A separação por herança de runtime_error e logic_error é mais idiomática e expressa a natureza do erro.
Saída esperada
Saldo inicial: 100
Saque de 150: Erro de saldo: saldo insuficiente
Saque de -10: Erro de valor: valor invalido
Saldo final: 100