Tratamento de Exceções em OOP
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 81 de 104.
O tratamento de exceções em C++ oferece uma maneira estruturada de detectar e responder a erros em tempo de execução. Na programação orientada a objetos, as exceções se integram naturalmente às hierarquias de classes, permitindo que você crie tipos de erro significativos que carregam contexto sobre o que deu errado.
O mecanismo básico usa três palavras-chave: try, throw e catch. O código que pode falhar fica em um bloco try, os erros são sinalizados com throw, e os blocos catch tratam tipos específicos de exceção:
#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";
}A biblioteca padrão do C++ fornece uma hierarquia de classes de exceção com raiz em std::exception. As classes derivadas comuns incluem std::runtime_error para problemas em tempo de execução e std::logic_error para erros de programação. Todas as exceções padrão fornecem um método what() que retorna uma mensagem de erro.
Quando uma exceção é lançada, a pilha é desenrolada — os destruidores são chamados para todos os objetos locais enquanto o programa procura um bloco catch correspondente. Isso garante a limpeza adequada mesmo quando ocorrem erros, e é por isso que RAII e o tratamento de exceções funcionam tão bem juntos.
Desafio
FácilVamos criar uma calculadora de divisão segura que demonstra o tratamento de exceções em um contexto orientado a objetos. Você criará uma classe Calculator que realiza operações de divisão e lança exceções significativas quando algo dá errado; depois, tratará essas exceções de forma adequada no programa principal.
Você organizará seu código em três arquivos:
Calculator.h: Defina sua classeCalculator, que realiza operações matemáticas com o tratamento adequado de erros.Seu
Calculatordeve ter um métododivide(double numerator, double denominator)que retorna o resultado da divisão. No entanto, se o denominador for zero, ele deverá lançar umastd::runtime_errorcom a mensagemDivision by zero. Se o numerador ou o denominador for negativo, ele deverá lançar umastd::invalid_argumentcom a mensagemNegative values not allowed.Inclua também um método
safeSqrt(double value)que retorna a raiz quadrada do valor. Se o valor for negativo, lance umastd::runtime_errorcom a mensagemCannot compute square root of negative number. Você precisará incluir<cmath>para usar a funçãosqrt.Calculator.cpp: Implemente os métodos declarados no arquivo de cabeçalho. Inclua os cabeçalhos necessários (<stdexcept>e<cmath>) e verifique as condições antes de realizar as operações.main.cpp: Leia três entradas (cada uma em uma linha separada):- Tipo de operação:
divideousqrt - Primeiro número (double)
- Segundo número (double): usado somente para divisão, ignorado para sqrt
Crie um objeto
Calculatore realize a operação solicitada dentro de um bloco try. Se a operação for bem-sucedida, imprimaResult: [value]. Se uma exceção for capturada, imprimaError: [message], em que a mensagem vem do métodowhat()da exceção.Após o bloco try-catch (independentemente de ter ocorrido uma exceção), imprima
Calculation completepara demonstrar que o programa continua sendo executado após tratar as exceções.- Tipo de operação:
Por exemplo, com as entradas divide, 10 e 2:
Result: 5
Calculation completeCom as entradas divide, 10 e 0:
Error: Division by zero
Calculation completeCom as entradas divide, -5 e 2:
Error: Negative values not allowed
Calculation completeCom as entradas sqrt, 16 e 0:
Result: 4
Calculation completeCom as entradas sqrt, -9 e 0:
Error: Cannot compute square root of negative number
Calculation completeObserve como o programa trata os erros de forma adequada, capturando exceções e exibindo mensagens significativas, e depois continua sua execução. Esse padrão mantém o tratamento de erros organizado e sua lógica principal concentrada no caminho de execução esperado.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Calculator.h"
using namespace std;
int main() {
// Ler entradas
string operation;
double num1, num2;
cin >> operation;
cin >> num1;
cin >> num2;
// Criar objeto Calculator
Calculator calc;
// TODO: Use um bloco try-catch para realizar a operação
// - Se operation for "divide", chame calc.divide(num1, num2)
// - Se operation for "sqrt", chame calc.safeSqrt(num1)
// - Em caso de sucesso, imprima "Result: " seguido do resultado
// - Capture as exceções e imprima "Error: " seguido da mensagem da exceção (use what())
// - After the try-catch, always print "Calculation complete"
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online