Tratamento de Exceções em POO
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 81 de 104.
Tratamento de exceções em C++ fornece uma maneira estruturada de detectar e responder a erros de tempo de execução. Na programação orientada a objetos, as exceções se integram naturalmente com hierarquias de classes, permitindo que você crie tipos de erro significativos que carregam contexto sobre o que deu errado.
O mecanismo básico utiliza três palavras-chave: try, throw e catch. O código que pode falhar fica dentro de um bloco try, os erros são sinalizados com throw e os blocos catch lidam com 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 originadas em std::exception. Classes derivadas comuns incluem std::runtime_error para problemas de 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 destrutores são chamados para todos os objetos locais enquanto o programa procura por um bloco catch correspondente. Isso garante a limpeza adequada mesmo quando ocorrem erros, razão pela qual RAII e o tratamento de exceções funcionam tão bem juntos.
Desafio
FácilVamos construir 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, tratando essas exceções de forma adequada em seu programa principal.
Você organizará seu código em três arquivos:
Calculator.h: Defina sua classeCalculatorque realiza operações matemáticas com o tratamento de erros adequado.Sua
Calculatordeve ter um métododivide(double numerator, double denominator)que retorna o resultado da divisão. No entanto, se o denominador for zero, ele deve lançar umstd::runtime_errorcom a mensagemDivision by zero. Se o numerador ou o denominador for negativo, ele deve lançar umstd::invalid_argumentcom a mensagemNegative values not allowed.Também inclua um método
safeSqrt(double value)que retorna a raiz quadrada do valor. Se o valor for negativo, lance umstd::runtime_errorcom a mensagemCannot compute square root of negative number. Você precisará incluir<cmath>para a funçãosqrt.Calculator.cpp: Implemente os métodos declarados em seu arquivo de cabeçalho. Certifique-se de incluir 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 apenas 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]onde 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 a execução após o tratamento das 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 lida com erros de forma elegante, capturando exceções e exibindo mensagens significativas, e depois continua a execução. Esse padrão mantém o tratamento de erros limpo e a lógica principal focada no fluxo normal de execução.
Folha de consulta
O tratamento de exceções em C++ utiliza três palavras-chave: try, throw e catch. O código que pode falhar fica dentro de um bloco try, os erros são sinalizados com throw, e os blocos catch tratam tipos específicos de exceção:
try {
// Code that might throw an exception
account.withdraw(150.0);
} catch (const std::runtime_error& e) {
std::cout << "Error: " << e.what() << "\n";
}Lançando exceções a partir de métodos de classe:
void withdraw(double amount) {
if (amount > balance) {
throw std::runtime_error("Insufficient funds");
}
balance -= amount;
}A biblioteca padrão do C++ fornece classes de exceção baseadas em std::exception. Os tipos comuns incluem:
std::runtime_error- para problemas em tempo de execuçãostd::logic_error- para erros de lógica de programaçãostd::invalid_argument- para argumentos inválidos
Todas as exceções padrão fornecem um método what() que retorna uma mensagem de erro.
Quando uma exceção é lançada, ocorre o desenrolar da pilha (stack unwinding) - os destrutores são chamados para todos os objetos locais enquanto o programa procura por um bloco catch correspondente, garantindo a limpeza adequada dos recursos.
Inclua <stdexcept> para usar as classes de exceção padrão.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Calculator.h"
using namespace std;
int main() {
// Ler as entradas
string operation;
double num1, num2;
cin >> operation;
cin >> num1;
cin >> num2;
// Criar o 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 pelo resultado
// - Capture exceções e imprima "Error: " seguido pela mensagem da exceção (use what())
// - Após o try-catch, sempre imprima "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 OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas11Conceitos Avançados de POO
Composição vs HerançaMixins via CRTPPimpl IdiomType ErasureEnum Classes e Tipagem ForteTratamento de Exceções em POOHierarquias de Exceções Personalizadas3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico