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 geralmente se beneficiam da criação de hierarquias de exceções 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 substitui 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) {}
};Essa hierarquia permite que os blocos catch tratem 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 ao mais geral, pois C++ usa o primeiro manipulador correspondente.
Desafio
FácilVamos criar um sistema de processamento de arquivos com uma hierarquia de exceções personalizada que lida com diferentes tipos de erros relacionados a arquivos. Você criará exceções específicas do domínio que herdam de uma classe de exceção base, permitindo o tratamento preciso de erros em diferentes níveis de especificidade.
Você organizará seu código em três arquivos:
FileExceptions.h: Defina sua hierarquia de exceções personalizada para operações com arquivos.Crie uma classe base
FileExceptionque herde destd::exception. Ela deve armazenar uma mensagem de erro e sobrescrever o métodowhat()com o especificadornoexcept.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 da classe pai com a mensagem apropriada.
FileProcessor.h: Defina uma classeFileProcessorque simula operações com arquivos e lança suas exceções personalizadas.Seu
FileProcessordeve ter um métodoopenFile(const std::string& filename)que simula a abertura de um arquivo:- Se o nome do arquivo contiver
missing, lance umaFileNotFoundException - Se o nome do arquivo contiver
locked, lance umaPermissionDeniedException - Caso contrário, retorne a string
Successfully opened: [filename]
- Se o nome do arquivo contiver
main.cpp: Leia um único nome de arquivo da entrada.Crie um
FileProcessore tente abrir o arquivo. Use um bloco try-catch com os manipuladores ordenados do mais específico ao mais geral:- Capture
FileNotFoundExceptione imprima:File Error: [message] - Capture
PermissionDeniedExceptione imprima:Access Error: [message] - Capture
FileExceptione imprima:General File Error: [message]
Se nenhuma exceção for lançada, imprima a mensagem de sucesso retornada por
openFile().- Capture
Por exemplo, com a entrada report.txt:
Successfully opened: report.txtCom a entrada missing_data.csv:
File Error: File not found: missing_data.csvCom a entrada locked_config.ini:
Access Error: Permission denied: locked_config.iniEste desafio demonstra como hierarquias de exceções personalizadas permitem tratar 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 de maneira uniforme.
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 handlers 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 na ordem do mais específico para o mais geral
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