Linguagem de Programação II Carlos Oberdan Rolim Ciência da Computação Sistemas de Informação Tratamento de exceções Tratamento de exceções Mecanismo que permite tratar de erros em tempo de execução de modo mais controlado, simplificado e eficiente que as formas tradicionais de detecção e tratamento de erros Trata dos mecanismos de disparo, captura e tratamento de exceções Problemas durante a execução de um programa Exceções Exceções são erros que ocorrem durante a execução de um programa. Normalmente, muitos desses erros podem ser previstos pelo programador que utiliza funções próprias para trata-los Exceções em C++ constituem um novo mecanismo, uma ferramenta poderosa e flexível para simplificar a detecção e tratamento de erros durante a execução do programa Modo tradicional Forma tradicional uma função retorna um valor determinando a ocorrência de um erro. Necessita verificar o valor retornado todas as vezes Com o uso de classes e objetos como saber se ocorreu um erro durante a execução do construtor ou do destrutor ? O mecanismo de exceções Exceção em C++ é uma resposta a uma situação anormal que surge enquanto um programa está em execução Exemplo: divisão por zero Podem provocar a transferência do controle para uma parte do programa para outra parte. Envolve três conceitos: Utilização de um bloco try (try) Captura da uma exceção por um manipulador de exceções (catch) Disparo de uma exceção (throw) O mecanismo de exceções Envolver o bloco suspeito de gerar exceção com um try. Começa com um comando try seguido do código entre chaves Pode ser seguido de um ou mais catch A captura de uma exceção é feita por um catch. Começa com a palavra catch seguido seguido por uma declaração de tipo associada ao tipo de exceção a qual responde Corpo igual a uma função ou método normal Será disparado por um throw Throw indica o disparo de uma exceção quando surge algum problema implicando o desvio para um outro ponto (bloco catch) É seguido de um valor (string, constante, obj, etc) que indica o tipo de exceção O mecanismo de exceções Bloco try S Exceção ? N Bloco catch Int main ( ) { Int dividendo, divisor, quociente; cin >> dividendo; cin >> divisor; try { if (divisor == 0) throw “Tentativa de divisão por zero”; quociente = dividendo / divisor; cout << “Resultado” << dividendo; }catch (const char * msg) { cout << “Erro :” << msg << endl; } return 1; } Exemplo básico de tratamento de exceção Informações adicionais Um try pode por meio de um throw disparar mais de uma exceção Disparo de throw lembra um return mas ao invés de retornar o controle a função chamadora faz com que o programa vá para o catch correspondente No bloco catch( const char *msg) existe a correspondencia com a exceção disparada que é uma string. Declaração de msg assemelha-se a um argumento de função Caso um bloco try seja executado sem disparar exceções os blocos catch não serão executados. Caso haja o disparo de exceção o bloco catch será executado e o programa continuará após o último bloco catch Int main ( ) { Int dividendo, divisor, quociente; cin >> dividendo; cin >> divisor; try { if (divisor == 0) throw “Tentativa de divisão por zero”; if (divisor < 0) throw 8888; quociente = dividendo / divisor; cout << “Resultado” << dividendo; }catch (const char * msg) { cout << “Erro :” << msg << endl; } }catch (int erro) { cout << “Erro :” << erro << endl; } return 1; } Exemplo disparando mais de uma exceção Exceções e funções A definição de uma função pode ser qualificada com a especificação da exceção para indicar quais tipos de exceção essa função pode disparar E um bloco catch poderá responder a mais de uma exceção Para qualificar a função usar nomeFunção (argumentos) throw (excessção1, exceção2,...) Exemplo int calcular(int valor1, int valor2) throw (const char *); Ou int calcular(int valor1, int valor2) throw (const char *, int); ..... double calcRaiz(double) throw (const char *); double calcRaiz(double numero) throw (const char *){ int main () { double numero, raizq; if (numero == 0) throw “Raiz de num negativo”; cin >> numero; return sqrt(numero); try { raizq = calcRaiz(numero); cout << “Raiz:” << raizq; } } catch ( const char * msg) { cout << “Erro ” << msg << endl; } cout << “Termino do programa” << end”; return 1; } Exemplo de código com função qualificada com exceção Múltiplos blocos try Pode-se utilizar vários blocos try dentro de um programa .... cin >> numero; try { raizq = calcRaiz(numero); cout << “Raiz:” << raizq; } catch ( const char * msg) { cout << “Erro ” << msg << endl; } try { raizq = Dobro(numero); cout << “Dobro:” << dobro; } catch ( const char * msg) { cout << “Erro ” << msg << endl; } Captura de qualquer exceção É possível usar um bloco catch “genérico” que é executado quando qualquer exceção for disparada Basta escrever ... (reticencias) no lugar do tipo de exceção Int main ( ) { Int dividendo, divisor, quociente; cin >> dividendo; cin >> divisor; try { if (divisor == 0) throw “Tentativa de divisão por zero”; if (divisor < 0) throw 8888; quociente = dividendo / divisor; cout << “Resultado” << dividendo; }catch ( ...) { cout << “Ocorreu uma exceção << endl; } return 1; } Exemplo com catch genérico capturando qualquer tipo de exceção Exceções e classes Exceções podem ser aplicadas em classes com algumas diferenças Por exemplo um construtor poderia disparar uma exceção quando uma chamada ao new fosse mal sucedida Na utilização de exceções associadas a classes a prática é disparar objetos como exceções e captura-los com referência Para um método que dispara uma exceção, define-se uma classe de exceção dentro da classe principal para ser utilizada como o tipo da exceção disparada Exceções e classes Declaração de classe dentro da classe é conveniente, pois o tipo de exceção indica a classe que a originou, além de ajudar a evitar nomes conflitantes class vetor { public: class erro { }; private: .... }; Assim será possível disparar uma exceção do tipo throw erro(); a ser tratada por um bloco catch ( vetor :: erro ) { ... } class numExtenso { public: ler(); class Erro { }; private: int numero; }; void numExtenso :: ler () { cin << numero ; if (numero <1 || numero > 5) throw Erro; } int main () { numExtenso num; try { num.ler(); } catch(numExtenso :: Erro ){ cout << “Numero fora do invervalo”; return 1; } return 0; } Exemplo com classe de exceção vazia class numExtenso { public: ler(); class Erro { public: Erro(); int indice; }; private: int numero; }; int main () { numExtenso num; try { num.ler(); } void numExtenso :: ler () { cin << numero ; if (numero <1 || numero > 5) throw Erro(numero); } numExtenso :: Erro :: Erro (int i){ indice = i; } // catch(numExtenso :: Erro ){ catch(numExtenso :: Erro & i ){ cout << i. indice << “ fora do invervalo”; return 1; } return 0; Exemplo com classe de exceção com membros } Exceções e o operador new Com o mecanismo de exceção quando a alocação de memória com o operador new for mal sucesidade será disparada uma exceção do tipo bad_alloc que é uma classe derivada da classe exception. Em função da derivação bad_alloc herda um método virtual chamado what() que retorna um string indicativo da ocorrência de uma exceção Essa string varia de acordo com implementação do C++ struct conta { double saldo[5000]; }; Int main ( ) { conta * ptr; try { cout << “operador new tenta alocar espaco em memória”; ptr = new conta[10000]; } catch ( bad_alloc & msg){ cout << String do metodo what(): “<< msg.what() << endl; return 1; } Saída: delete [ ] ptr; O operador new tenta alocar espaço em memória return 0; } String do método what(): st9bad_alloc Exemplo de exceção com alocação dinâmica