Tratamento de exceções: Parte I

Formas Tradicionais de Tratamento de Erro, sem Exceções

Para tratar e propagar erros, normalmente temos as seguintes opções:

  1. Terminar o Programa
    O método mais brusco e espartano. Não é bom porque alguns erros você quer tratar. Você faz isso normalmente com abort(); exit();
  2. Devolver um código de erro.
    Nem sempre é viável porque você pode não poder devolver um código de erro, seja porque esta devolvendo um valor que não pode ser invalido ou porque você não tem controle sobre o retorno.
  3. Devolver um valor valido e deixar o objeto em um estado invalido.
    Usado para testar depois os erros. Pode dar um certo trabalho manter essa estrategia thread-safe. Em “C”, você tem como exemplo a variável “errno”.
  4. Chamar uma função de erro especifica.
    Você cadastra uma função para cada tipo de erro que você vai encontrar.

 Sem tratamento de exceções, todas as formas de código acima sofrem do seguinte mal:

  • Propagação de erro manual através das chamadas de função(através da pilha de chamadas).
  • Erros não identificados continuam o programa, deixando em um estado invalido e difícil de depurar.
  • O código fica com a legibilidade prejudicada pelo código de tratamento de erro.

Tratando erro com exceções

  • Tratamento de erros separado da logica do programa.
    Você ganha um código mais legível.
  • Erros não identificados terminaram o programa.
    O programa não fica em um estado invalido que se propaga ate um ponto critico, e também ajuda a descobrir a origem do erro de forma clara.
  • E tratado apenas por quem sabe tratar o erro.
    Não há o risco de um erro não identificado ou não planejado cair em um tratamento destinado a outro tipo de erro.
  • Facilita o tratamento de erros.
    Separa o tratamento de um tipo de erro em apenas um local do código. Deixa mais fácil a manutenção do código de tratamento e a legibilidade.
  • Deixa mais simples a propagação de erros entre as camadas mais acima do código.

Mas, O que é uma exceção em C++?

É uma estrutura de controlo não local, baseada em pilha. Equivale a um retorno alternativo, feito com objetivo de propagar erros que ocorrem no programa que são “exceções” ao seu funcionamento normal.

É usada principalmente para tratamento de erros e suporte a tolerância a falhas.

C++” tem suporte a exceções e tem inclusive uma biblioteca padrão para exceções. Dentro dessa biblioteca, temos a classe “exception“, que é definida assim:

class exception{
	public:
		exception() throw();
		exception(const exception&) throw();
		exception& operator=(const exception&) throw();
		virtual ~exception() throw();
		virtual const char *what() const throw();
};

Capturando ou lançando exceções

Para capturar ou lançar exceções, você pode usar algo assim:

try{ // Começa bloco que vai gerenciar exceções.
	// Assim você dispara exceções. Pode ser qualquer objeto, mas é bom
	// que seja uma que tenha parentesco com "exception" ou que seja "exception".
	throw exception("ERROR!!"); // Criamos neste exemplo um do tipo exception
} //Termina bloco que vai gerenciar exceções. Começa o tratamento de cada exceção
catch( H ){ // Trata exceções do tipo "H" ou filhas, podia ser outro tipo.
	// Aqui dentro tratamos o erros para tipos "H".
}
catch( exception e ){ // Trata exceções do tipo exception ou filhas.
	// Aqui dentro tratamos o erros para tipos "exception".
}

O código que você escreveu para  “catch( H )” só sera executado se:

  1. Se “H” for igual a “exception”.
    Por exemplo: typedef exception H;
  2. Se “H” for base de publica não ambígua.
    Problema do diamante(problema de herança).
  3. exception e “H” forem ponteiros que satisfazem [1] e [2].
  4. Se “H” for referencia do tipo que satisfaz [1] e [2] em relação ao tipo de exception.

Recebendo informações de uma exceção

Sempre que você receber uma exceção que seja ou derive da classe “exception“, você pode usar o método “what();” para saber informações sobre a exceção, que sera retornado na forma de um “const char*“:

catch( std::exception &e ){
	std::cerr << e.what() << std::endl ; // Imprime na saida de erro informações sobre erro
}

 Caso você tenha lançado um objeto seu criado para o “throw“, você pode pegar qualquer atributo dele. Esse caso é para as classes derivadas de “exception“, que é o padrão.

Aviso:

Sempre passe para o catch por referencia:

catch( MyType &e ){

Se não pode haver problemas de herança.

Capturando exceções genéricas

Caso seja necessário capturar uma exceção que você não saiba de que tipo é(como por exemplo para fazer alguma finalização antes de propagar o erro), você pode fazer:

catch( ... ){
	std::cerr << "WTF?" << std::endl ;
	exit(1);
}

Redisparando exceções

Você pode precisar redisparar a exceção que você capturou para uma camada mais a cima do programa quando você não conseguir tratar por completo o erro, ou quando quiser que uma camada mais a cima do programa trate o erro ou encerre o programa.

Você pode fazer isso assim:

try{  .........   }
catch( ... ){
	delete x;
	throw; // Relança exceção não tratável aqui....
}

O que acontece quando as exceções não são tratadas por um “catch”?

O programa simplesmente acaba.

A exceção vai voltando nas chamadas de função, sempre em ordem contraria as funções chamadas até encontrar um “catch” que a segure. Caso isso não ocorra, ela chega no main e quando tenta voltar no main, acaba terminado o programa.

Assim você tem certeza que seu programa não vai estar em um estado invalido com um erro não tratado, ou terminar em um ponto muito depois do erro que seja muito difícil de depurar o problema.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: