Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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 classe Calculator que realiza operações matemáticas com o tratamento de erros adequado.

    Sua Calculator deve ter um método divide(double numerator, double denominator) que retorna o resultado da divisão. No entanto, se o denominador for zero, ele deve lançar um std::runtime_error com a mensagem Division by zero. Se o numerador ou o denominador for negativo, ele deve lançar um std::invalid_argument com a mensagem Negative 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 um std::runtime_error com a mensagem Cannot compute square root of negative number. Você precisará incluir <cmath> para a função sqrt.

  • 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):
    1. Tipo de operação: divide ou sqrt
    2. Primeiro número (double)
    3. Segundo número (double) — usado apenas para divisão, ignorado para sqrt

    Crie um objeto Calculator e realize a operação solicitada dentro de um bloco try. Se a operação for bem-sucedida, imprima Result: [value]. Se uma exceção for capturada, imprima Error: [message] onde a mensagem vem do método what() da exceção.

    Após o bloco try-catch (independentemente de ter ocorrido uma exceção), imprima Calculation complete para demonstrar que o programa continua a execução após o tratamento das exceções.

Por exemplo, com as entradas divide, 10 e 2:

Result: 5
Calculation complete

Com as entradas divide, 10 e 0:

Error: Division by zero
Calculation complete

Com as entradas divide, -5 e 2:

Error: Negative values not allowed
Calculation complete

Com as entradas sqrt, 16 e 0:

Result: 4
Calculation complete

Com as entradas sqrt, -9 e 0:

Error: Cannot compute square root of negative number
Calculation complete

Observe 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ção
  • std::logic_error - para erros de lógica de programação
  • std::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;
}
quiz iconTeste seus conhecimentos

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