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.
Desafio
FácilVamos 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
FileExceptionque herda 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 de sua classe pai com a mensagem apropriada.
FileProcessor.h: Defina uma classeFileProcessorque simula operações de arquivo 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 manipuladores ordenados do mais específico para o 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 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;
}
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