Menu
Coddy logo textTech

Hierarquias de Exceções Personalizadas

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 82 de 104.

Embora a biblioteca padrão forneça classes de exceção úteis como std::runtime_error, aplicações do mundo real frequentemente se beneficiam da criação de hierarquias de exceção personalizadas. Ao definir suas próprias classes de exceção que herdam de exceções padrão, você pode criar tipos de erro específicos do domínio que carregam contexto adicional e permitem um tratamento de erros mais preciso.

Uma classe de exceção personalizada normalmente herda de std::exception ou de uma de suas classes derivadas e sobrescreve o método what():

#include <exception>
#include <string>

class DatabaseException : public std::exception {
    std::string message;
public:
    DatabaseException(const std::string& msg) : message(msg) {}
    const char* what() const noexcept override { return message.c_str(); }
};

class ConnectionException : public DatabaseException {
public:
    ConnectionException(const std::string& host) 
        : DatabaseException("Failed to connect to: " + host) {}
};

class QueryException : public DatabaseException {
public:
    QueryException(const std::string& query) 
        : DatabaseException("Invalid query: " + query) {}
};

Esta hierarquia permite que blocos catch lidem com exceções em diferentes níveis de especificidade:

try {
    // operações de banco de dados...
    throw ConnectionException("localhost");
} catch (const ConnectionException& e) {
    std::cout << "Connection issue: " << e.what() << "\n";
} catch (const DatabaseException& e) {
    std::cout << "Database error: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cout << "General error: " << e.what() << "\n";
}

O especificador noexcept em what() é importante - ele garante que o método não lançará exceções, o que é exigido pela interface da classe base. Ordene seus blocos catch do mais específico para o mais geral, pois o C++ usa o primeiro manipulador correspondente.

challenge icon

Desafio

Fácil

Vamos construir um sistema de processamento de arquivos com uma hierarquia de exceções personalizadas que lida com diferentes tipos de erros relacionados a arquivos. Você criará exceções específicas de domínio que herdam de uma classe de exceção base, permitindo um tratamento de erros preciso em diferentes níveis de especificidade.

Você organizará seu código em três arquivos:

  • FileExceptions.h: Defina sua hierarquia de exceções personalizadas para operações de arquivo.

    Crie uma classe base FileException que herda de std::exception. Ela deve armazenar uma mensagem de erro e sobrescrever o método what() com o especificador noexcept.

    Em seguida, crie duas classes de exceção derivadas:

    • FileNotFoundException — recebe um nome de arquivo e cria uma mensagem: File not found: [filename]
    • PermissionDeniedException — recebe um nome de arquivo e cria uma mensagem: Permission denied: [filename]

    Cada classe derivada deve chamar o construtor de sua classe pai com a mensagem apropriada.

  • FileProcessor.h: Defina uma classe FileProcessor que simula operações de arquivo e lança suas exceções personalizadas.

    Seu FileProcessor deve ter um método openFile(const std::string& filename) que simula a abertura de um arquivo:

    • Se o nome do arquivo contiver missing, lance uma FileNotFoundException
    • Se o nome do arquivo contiver locked, lance uma PermissionDeniedException
    • Caso contrário, retorne a string Successfully opened: [filename]
  • main.cpp: Leia um único nome de arquivo da entrada.

    Crie um FileProcessor e tente abrir o arquivo. Use um bloco try-catch com manipuladores ordenados do mais específico para o mais geral:

    1. Capture FileNotFoundException e imprima: File Error: [message]
    2. Capture PermissionDeniedException e imprima: Access Error: [message]
    3. Capture FileException e imprima: General File Error: [message]

    Se nenhuma exceção for lançada, imprima a mensagem de sucesso retornada por openFile().

Por exemplo, com a entrada report.txt:

Successfully opened: report.txt

Com a entrada missing_data.csv:

File Error: File not found: missing_data.csv

Com a entrada locked_config.ini:

Access Error: Permission denied: locked_config.ini

Este desafio demonstra como as hierarquias de exceções personalizadas permitem que você trate erros no nível apropriado de detalhe — capturando exceções específicas quando você precisa de um tratamento preciso, ou capturando a exceção base quando deseja tratar todos os erros de arquivo uniformemente.

Folha de consulta

Classes de exceção personalizadas herdam de std::exception ou de suas classes derivadas e sobrescrevem o método what():

#include <exception>
#include <string>

class DatabaseException : public std::exception {
    std::string message;
public:
    DatabaseException(const std::string& msg) : message(msg) {}
    const char* what() const noexcept override { return message.c_str(); }
};

Crie hierarquias de exceção herdando de exceções base personalizadas:

class ConnectionException : public DatabaseException {
public:
    ConnectionException(const std::string& host) 
        : DatabaseException("Failed to connect to: " + host) {}
};

Trate exceções em diferentes níveis de especificidade ordenando os blocos catch do mais específico para o mais geral:

try {
    throw ConnectionException("localhost");
} catch (const ConnectionException& e) {
    std::cout << "Connection issue: " << e.what() << "\n";
} catch (const DatabaseException& e) {
    std::cout << "Database error: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cout << "General error: " << e.what() << "\n";
}

O especificador noexcept em what() garante que o método não lançará exceções, o que é exigido pela interface da classe base.

Experimente você mesmo

#include <iostream>
#include <string>
#include "FileExceptions.h"
#include "FileProcessor.h"

using namespace std;

int main() {
    string filename;
    cin >> filename;
    
    FileProcessor processor;
    
    // TODO: Use um bloco try-catch para tratar exceções
    // Ordene os manipuladores do mais específico para o mais geral:
    // 1. Catch FileNotFoundException - imprima: "File Error: [message]"
    // 2. Catch PermissionDeniedException - imprima: "Access Error: [message]"
    // 3. Catch FileException - imprima: "General File Error: [message]"
    // Se não houver exceção, imprima a mensagem de sucesso de openFile()
    
    try {
        // TODO: Chame processor.openFile() e trate o resultado
        
    }
    // TODO: Adicione blocos catch em ordem do mais específico para o mais geral
    
    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