Buscar

16 - Capítulo 13 - Tratamento de Exceções

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 41 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 41 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 41 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

13 
Tratamento de exceções 
Objetivos 
• Usar try, throw e catch para detectar, indicar e tratar exceções, respectivamente. 
• Processar exceções não-capturadas e inesperadas. 
• Ser capaz de processar falhas de new. 
• Usar auto_ptr para prevenir perdas de memória. 
• Entender a hierarquia padrão de exceções. 
Nunca esqueço um rosto, mas no seu caso vou abrir uma exce ção. 
Groucho (Julius Henry) Marx 
Nenhuma regra é tão geral que não admita exceções. Robert Burton, The Anatomy of Melancholy 
É do senso comum pegar um método e experimentá-lo. Sefalhar admita francamente e tente outro. Mas acima de tudo, tente alguma coisa. 
Franklin Delano Roosevelt. 
Oh! Jogue fora a pior parte disso e deixe a parte mais pura com a outra metade. 
William Shakespeare 
Se eles estão correndo e não olham aonde estão indo, Tenho que sair de algum lugar e apanhá-los. Jerome David Salinger 
E ao se desculpar várias vezes de uma falha não piore essa falha pela desculpa. 
William Shakespeare. 
1 
Errar é humnano, perdoar é divino. 
Alexander Pope, An Essay on Criticism 
CAPfTULO 13 - TRATAMENTO DE EXCEÇÕES 697 
Visão geral 
13.1 Introdução 
13.2 Quando o tratamento de exceções deve ser usado 
13.3 Outras técnicas de tratamento de erros 
13.4 Fundamentos do tratamento de exceções em C++: try, throw, catch 
13.5 Um exemplo simples de tratamento de exceção: divisão por zero 
13.6 Disparando uma exceção 
13.7 Capturando uma exceção 
13.8 Disparando novamente uma exceção 
13.9 Especificações de exceção 
13.10 Processando exceções inesperadas 
13.11 Desempilhando a pilha 
13.12 Construtores, destruidores e o tratamento de exceções 
13.13 Exceções e herança 
13.14 Processando falhas de new 
13.15 A classe auto_ptr e a alocação dinâmica de memória 
13.16 Hierarquia de exceções da biblioteca padrão 
Resumo• Terminologia Erros comuns de programação • Boas práticas de programação. Observações de engenharia de software Dicas de desempenho Dicas de teste e depura ção Exercícios de auto-revisão Respostas aos exercícios de auto-revisão • Exercícios 
13.1 Introdução 
Neste capítulo, introduzimos o tratamento de exceções. A estensibilidade de C++ pode aumentar substancialmente a quantidade e os tipos de erros que podem acontecer. Os recursos apresentados aqui possibilitam aos programadores escreverem programas mais claros, mais robustos e tolerantes a falhas. Sistemas recentes desenvolvidos com estas e/ou técnicas semelhantes têm reportado resultados positivos. Também mencionaremos quando o tratamento de exceções não deve ser usado. 
O estilo e os detalhes do tratamento de exceções apresentados neste capítulo estão baseados no trabalho de Andrew Koenig e Bjarne Stroustrup apresentado em seu artigo “Exception Handling for C++ (revised)”, publicado nos Proceedings ofthe USENIX C++ Conference, ocorrida em São Francisco, em abril de 1990. 
O código de tratamento de erro varia em natureza e entre sistemas de software, dependendo de se o aplicativo de software é ou não um produto para lançamento comercial. Os produtos comerciais tendem a conter muito mais código de tratamento de erros do que o software “mais informal”. 
Existem muitos meios populares de se lidar com erros. Mais comumente, o código de tratamento de erro está misturado no meio do código de um sistema. Os erros são tratados nos lugares do código onde os erros podem acontecer. A vantagem desta abordagem é que um programador que estiver lendo o código pode ver o processamento de erro na vizinhança imediata do código e determinar se foi implementada a verificação apropriada de erro. 
O problema com este esquema é que o código, de certo modo, se torna “poluído” com o processamento de erros. Se torna mais difícil para um programador preocupado com o aplicativo em si ler o código e determinar se o mesmo está funcionando corretamente. Isto torna mais difícil de se entender e manter o código. 
Alguns exemplos comuns de exceções são uma falha de new em obter uma quantidade solicitada de memória, um subscrito de array fora dos limites, estouro em operações aritméticas, divisão por zero e parâmetros de função inválidos. 
698 C++ COMO PROGRAMAR 
Os recursos de tratamento de exceções de C++ possibilitam ao programador remover o código de tratamento de erros da “linha principal” de execução de um programa. Isso melhora a legibilidade e a possibilidade de modificar o programa. Com o estilo de C++ de tratamento de exceções, é possível se capturar todos os tipos de exceções, capturar todas as exceções de um certo tipo ou capturar todas as exceções de tipos relacionados. Isso torna os programas mais robustos, reduzindo a probabilidade de que erros não sejam capturados por um programa. O tratamento de exceções é fornecido para possibilitar aos programas capturar e tratar erros, em vez de deixá-los acontecer e sofrer as conseqüências. Se o programador não fornece um meio de tratamento para um erro fatal, o programa termina. 
O tratamento de exceções foi projetado para lidar com erros síncronos, tal como uma tentativa de dividir por zero (isso acontece quando o programa executa a instrução dividir). Com tratamento de exceções, antes de o programa executar a divisão, ele confere o denominador e “dispara” uma exceção se o denominador for zero. 
O tratamento de exceções não foi projetado para lidar com situações assíncronas, tais como término de EIS em disco, chegada de mensagem pela rede, diques de mouse, etc.; essas são mais bem tratadas através de outros meios, tal como interrupções de processamento. 
O tratamento de exceções é usado em situações em que o sistema pode recuperar o erro que causou a exceção. O procedimento que faz a recuperação é chamado de tratador de exceção. O tratamento de exceções é tipicamente usado quando o erro será tratado por uma parte diferente do programa (i.e., um escopo diferente) daquela que detectou o erro. Um programa que conduz um diálogo interativo com um usuário não deve usar exceções para processar erros de entrada. 
O tratamento de exceções é especialmente apropriado a situações em que o programa não será capaz de se recuperar, mas precisa fazer uma “limpeza final” organizada e então terminar “elegantemente”. 
Boa prática de programação 13.1 
Use exceções para erros que devem ser processados em um escopo diferente daquele em que ocorrem. Use 
outros meios de tratamento de erros para erros que serão processados no escopo em que acontecem. 
Boa prática de programação 13.2 
Evite usar tratamento de exceções para fins diferentes do tratamento de erros, pois isso pode reduzir a 
clareza do programa. 
Existe outra razão para se evitar usar técnicas de tratamento de exceções para o controle de programas convencionais. O tratamento de exceções foi projetado para processamento de erros, que é uma atividade infreqüente, comumente usada porque um programa está para terminar. Em vista disso, não se espera que os autores de compiladores C++ implementem o tratamento de exceções com o tipo de desempenho otimizado que se espera para o código regular de aplicativos. 
Dica de desempenho 13.1 
f Embora seja possível se usar o tratamento de exceções para propósitos diferentes do tratamento de erros, isso pode reduzir o desempenho do programa. 
Dica de desempenho 13.2 
f ‘ O tratamento de exceções é geralmente implementado em compiladores de tal maneira que quando não ocorrem exceções, pouco ou nenhum overhead é imposto pela presença do código de tratamento de exceções. Quando ocorrem exceções, elas incorrem em overhead durante a execução. Certamente, a presença de código de tratamento de exceções faz o programa consumir mais memória. 
Observação de engenharia de software 13.1 
______ O fluxo de controle com estruturas de controle convencionais é geralmente mais claro e eficiente do que com exceções. 
Erro comum de programação 13.1 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 699 
Uma outra razão segundo a qual exceções podem ser perigosas como uma alternativa ao fluxo normal de controle é que a pilha é desempilhada e os recursos alocados antes da ocorrência da exceção podem não ser liberados.Este problema pode ser evitado com uma programação cuidadosa. 
O tratamento de exceções ajuda a melhorar a tolerância a falhas de um programa. Torna-se “mais agradável” escrever código de processamento de erros e, assim, é mais provável que os programadores o façam. Também se torna possível capturar exceções de vários modos, tal como por tipo, ou até especificar que exceções de qualquer tipo devem ser capturadas. 
A maioria dos programas escritos atualmente suporta somente uma “única thread” (fluxo) de execução. O multithreading vem recebendo grande atenção em sistemas operacionais recentes, como Windows NT, OS/2 e várias versões do Unix. As técnicas discutidas neste capítulo se aplicam até para programas que usam multithreading, embora não discutamos especificamente programas que usam multithreading. 
Mostraremos como lidar com exceções “não-capturadas”. Consideraremos como são tratadas exceções inesperadas. Mostraremos como exceções relacionadas podem ser representadas por classes de exceções derivadas de uma classe base de exceção comum. 
Os recursos de tratamento de exceções de C++ estão se tornando amplamente usados, como resultado do padrão C++. A padronização é especialmente importante para grandes projetos de software, nos quais dezenas ou até centenas de pessoas trabalham em componentes separados de um sistema, e tais componentes necessitam interagir para o sistema global funcionar corretamente. 
Observação de engenharia de software 13.2 
O tratamento de exceções é bem adequado para sistemas com componentes desenvolvidos separadamente. O tratamento de exceções facilita a combinação dos componentes. Cada componente pode executar sua própria detecção de exceção, separada do tratamento das exceções em outro escopo. 
o tratamento de exceções pode ser visto como um outro meio de retornar o controle de uma função ou sair de um bloco de código. Normalmente, quando acontece uma exceção, ela será tratada por um invocador da função geradora da exceção, por um invocador daquele invocador, ou quão longe para trás na cadeia de chamadas for necessário se ir para achar um tratador para aquela exceção. 
13.2 Quando o tratamento de exceções deve ser usado 
O tratamento de exceções deve ser usado somente para processar situações excepcionais, apesar do fato de que não existe nada que impeça o programador de usar exceções como uma alternativa para o controle do programa; processar exceções para componentes de programa que não estão preparados para tratar aquelas exceções diretamente; processar exceções de componentes de software tais como funções, bibliotecas e classes que, provavelmente, serão amplamente usados e onde não faz sentido que esses componentes tratem suas próprias exceções; e em projetos de grande porte para tratar o processamento de erros de uma maneira uniforme em todo o projeto. 
Boa prática de programação 13.3 
Use técnicas de tratamento de erros convencionais em lugar do tratamento de exceções para um processamento de erros direto e local, no qual um programa pode facilmente lidar com seus próprios erros. 
Observação de engenharia de software 13.3 
Ao lidar com bibliotecas, o chamador dafirnção de biblioteca provavelmente terá em mente umprocessainento de erro especifico para uma exceção gerada na função de biblioteca. E improvável que uma função de biblioteca execute um processamento de erro que satisfaça às necessidades particulares de todos os usuários. Portanto, exceções são meios apropriados para tratar erros produzidos por funções de bibliotecas. 
700 C+÷ CoMo PROGRAMAR 
13.3 Outras técnicas de tratamento de erros 
Apresentamos uma variedade de modos de lidar com situações excepcionais anteriormente a este capítulo. O texto a seguir resume estas e outras técnicas úteis. 
• Use assert para testar erros de codificação e projeto. Se uma asserção for false. o programa termina e o código deve ser corrigido. Isto é útil durante a depuração do programa. 
• Simplesmente ignore as exceções. Isso seria devastador para produtos de software lançados para o público em geral, ou para software de finalidade especial - necessário para missões críticas. Mas para seu próprio 
software, desenvolvido para seus próprios fins, é bastante comum se ignorar muitos tipos de erros. 
• Aborte o programa. Isso, é claro, evita que um programa execute até a conclusão e produza resultados incorretos. Realmente, para muitos tipos de erros isto é apropriado, especialmente para erros não-fatais que possibilitam que um programa execute até a conclusão, talvez levando o programador a pensar que o programa funcionou corretamente. Aqui, também, tal estratégia é imprópria para aplicativos destinados a missões críticas. Os aspectos relacionados aos recursos também são importantes aqui. Se um programa obtém um recurso, o programa deveria liberar normalmente aquele recurso antes do término do programa. 
Erro comum de programação 13.2 
Abortar um programa pode deixar um recurso em um estado em que outros programas não são capazes de 
acessar o mesmo, e conseqüentemente o programa teria uma assim chamada “perda de recurso 
• Inicialize algum indicador de erro. O problema com esta solução é que os programas não podem verificar estes indicadores de erro em todos os pontos em que os erros poderiam ser problemáticos. 
• Teste a condição de erro, emita uma mensagem de erro e chame exit para passar um código de erro apropriado para o ambiente do programa. 
• Use setjump e longjump. Estas funções da biblioteca <csetjmp> possibilitam ao programador especificar um desvio imediato para fora de chamadas de funções profundamente aninhadas, de volta para um tratador de erro. Sem setjump /longjump. um programa deve executar vários returns para sair das chamadas de funções profundamente aninhadas. Isso poderia ser usado para desviar para algum tratador de erro. Mas elas são perigosas, porque elas desempilham a pilha sem chamar destruidores para objetos automáticos. Isso pode levar a sérios problemas. 
• Certos tipos específicos de erros têm recursos dedicados para o seu tratamento. Por exemplo, quando new falha na alocação de memória, pode fazer com que uma função new handier seja executada para tratar o erro. Esta função pode ser trocada fornecendo-se um nome de função como argumento para setnewhandler. Discutimos a função set newhandler em detalhes na Seção 13.14. 
13.4 Fundamentos do tratamento de exceções em C++: try, throw, catch 
O tratamento de exceções em C++ se destina a situações em que a função que descobre um erro está impossibilitada de tratá-lo. Tal função disparará (throw) uma exceção. Não existe nenhuma garantia de que existirá “qualquer coisa lá fora”, i.e., um tratador de exceção especificamente preparado para processar aquele tipo de exceção. Se existir, a exceção será capturada e tratada. Se não existir nenhum tratador de exceção para aquele tipo particular de exceção, o programa termina. 
O programador coloca dentro de um bloco try o código que pode gerar um erro que produzirá uma exceção. O bloco try é seguido por um ou mais blocos catch. Cada bloco catch especifica o tipo de exceção que ele pode capturar e tratar. Cada bloco catch contém um tratador de exceção. Se a exceção corresponde ao tipo do parâmetro em um dos blocos catch, o código daquele bloco catch é executado. Se nenhum tratador for encontrado, é chamada a função terminate. a qual, por default. chama a função abort. 
Quando uma exceção é disparada, o controle do programa sai do bloco try e pesquisa os blocos catch em 
busca de um tratador apropriado (logo discutiremos o que torna um tratador “apropriado”). Se nenhuma exceção foi 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 701 
disparada no bloco try, os tratadores de exceção para aquele bloco são saltados e o programa retoma a execução depois do último bloco catch. 
Podemos especificar as exceções que uma função dispara. Como opção, podemos especificar que uma função 
não disparará nenhuma exceção. 
A exceção é disparada em um bloco try na função, ou a exceção é disparada a partir de uma função chamada diretamenteou indiretamente a partir do bloco try. O ponto em que o throw é executado é chamado de ponto de disparo. Este termo também é usado para descrever a própria expressão throw. Depois que uma exceção é disparada, o controle não pode retornar ao ponto de disparo da mesma. 
Quando ocorre uma exceção, é possível passar informações para o tratador de exceção, a partir do ponto em 
que ocorreu a exceção. Essa informação é o tipo do próprio objeto disparado ou informações colocadas no objeto disparado. 
O objeto disparado é tipicamente um string de caracteres (para uma mensagem de erro) ou um objeto de uma classe. O objeto disparado leva informações para o tratador de exceção que processará aquela exceção. 
Observação de engenharia de software 13.4 
Uma idéia-chave do tratamento de exceções é que a parte de um programa ou sistema que tratará a exceção pode ser bastante diferente ou distante da parte do programa que descobriu e gerou a situação excepcional. 
13.5 Um exemplo simples de tratamento de exceção: divisão por zero 
Agora, iremos considerar um exemplo simples de tratamento de exceções. A Fig. 13.1 usa try, throw e catch para descobrir que ocorreu uma divisão por zero, indicar uma exceção de divisão por zero e tratar uma exceção de divisão por zero. 
1 II Fig. 13.1: figl3Ol.cpp 
2 II Um exemplo simples de tratamento de exceção. 
3 // Verificando uma exceção de divisão por zero. 
4 #include <iostream> 
5 
6 using std::cout; 
7 using std::cin; 
8 using std: :endl; 
9 
II Classe DivideByZeroException a ser usada no tratamento de 1/ exceção para disparar uma exceção em caso de divisão por zero. class DivideByZeroException 
public: 
DivideByZeroException O 
message( ‘tentou dividir por zero” 
const char *what() const { return message; 
private: 
const char *message; 
// Definição da função quociente. Demonstra o disparo de uma // exceção quando uma exceção de divisão por zero é encontrada. double quotient( int numerator, int denominator 
if ( denominator == O 
throw DivideByZeroExceptionO; 
28 return static_cast< double > ( numerator ) / denominator; 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
Fig. 13.1 Um exemplo simples de tratamento de exceção com divisão por zero (parte 1 de 2). 
702 C++ COMO PROGRAMAR 
29 } 
30 
31 // Programa de teste 
32 int main() 
33 
34 int numberi, number2; 
35 double result; 
36 
37 cout « “Digite dois inteiros (fim de arquivo para terminar) : 
38 
39 while ( cm » nuxnberl » number2 
40 
41 // o bloco try engloba o código que pode disparar 
42 II uma exceção e o código que não deve ser executado 
43 II se ocorrer uma exceção 
44 try{ 
45 result = quotient( numberi, number2 ); 
46 cout « “O quociente é: « result « endl; 
47 
48 catch ( DivideByZeroException ex ) { II exception handler 
49 cout « “Ocorreu uma exceção: “ « ex. what () « ‘\n 
50 
51 
52 cout « “\nDigite dois inteiros (fim de arquivo para terminar) 
53 
54 
55 cout « endl; 
56 return 0; // termina normalmente 
57 
Fig. 13.1 Um exemplo simples de tratamento de exceção com divisão por zero (parte 2 de 2). 
Agora, considere o programa de teste em main. Note a declaração “localizada” de numberl e nuniber2. 
O programa contém um bloco try (linha 44) que engloba o código que pode disparar uma exceção. Note que a divisão real, que pode causar o erro, não é explicitamente listada dentro do bloco try. Em vez disso, a chamada 
para a função quotient (definida na linha 23) invoca o código que tenta a divisão real. A função quotient realmente dispara o objeto de exceção divisão por zero, como veremos em seguida. Em geral, os erros podem aparecer através de código explicitamente mencionado no bloco try. através de chamadas a uma função ou até através de chamadas de função profundamente aninhadas iniciadas por código no bloco try. 
O bloco try é imediatamente seguido por um bloco catch contendo o tratador de exceção para o erro divisão por zero. Em geral, quando uma exceção é disparada dentro de um bloco try. a exceção é capturada por um bloco catch que especifica o tipo apropriado que corresponde à exceção disparada. Na Fig. 13.1, o bloco catch especifica que ele capturará objetos de exceção do tipo DivideByZeroException; este tipo corresponde ao tipo do objeto associado à função quotient. O corpo desse tratador de exceção imprime a mensagem de erro retornada pela chamada da função what. Tratadores de exceção podem ser muito mais elaborados que esse. 
Digite dois O quociente 
inteiros (fim de é: 14.2857 
arquivo 
para terminar): 
100 
7 
Digite dois Ocorreu uma 
inteiros (fim de exceção: tentou 
arquivo dividir 
para terminar): por zero 
100 
O 
Digite dois O quociente 
inteiros (fim de é: 3.66667 
arquivo 
para terminar): 
33 9 
Digite dois 
inteiros (fim de 
arquivo 
para terminar): 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 703 
Se, ao ser executado, o código em um bloco try não dispara uma exceção, então todos os tratadores catch imediatamente após o bloco try são saltados e a execução continua na primeira linha de código depois dos tratadores catch; na Fig. 13.1, é executado um comando return que retorna O, indicando término normal. 
Agora, iremos examinar as definições da classe DivideByzeroException e da função quotient. Na função quotient, quando o comando if determina que o denominador é zero, o corpo do comando if executa um comando throw que especifica o nome do construtor para o objeto de exceção. Isto faz com que um objeto da classe DivideByZeroException seja criado. Este objeto será capturado pelo comando catch (que especifica o tipo DivideByzeroException) depois do bloco try. O construtor para a classe DivideByZeroException simplesmente aponta o membro de dados message para o string “tentou dividir por zero”. O objeto disparado é recebido no parâmetro especificado no tratador catch (nesse caso, o parâmetro ex) e a mensagem é impressa lá através de uma chamada à função what O. 
Boa prática de programação 13.4 
Associar cada tipo de erro durante a execução a um objeto de exceção apropriadamente nomeado melhora a clareza do programa. 
13.6 Disparando uma exceção 
A palavra-chave throw é usada para indicar que uma exceção aconteceu. Isto é chamado de disparar uma exceção. 
Um throw normalmente especifica um operando (um caso especial que discutiremos não especifica nenhum ope rando) O operando de um throw pode ser de qualquer tipo. Se o operando é um objeto, chamamo-lo de objeto de 
exceção. O valor de qualquer expressão pode ser disparado em vez de um objeto. E possível se disparar objetos não 
voltados ao tratamento de erro. 
Onde uma exceção é capturada? Ao ser disparada, a exceção será capturada pelo tratador de exceção mais 
próximo (do bloco try do qual a exceção foi disparada) que especifique o tipo apropriado. Os tratadores de exceção 
para um bloco try são listados imediatamente após o bloco try. 
• Como parte do disparo de uma exceção, uma cópia temporária do operando do throw é criada e inicializada. 
A Esse objeto então inicializa o parâmetro no tratador de exceção. O objeto temporário é destruído quando o tratador 
de exceção completa a execução e o controle sai dele. F 
Observação de engenharia de software 13.5 
______ Se é necessário passar informações sobre o erro que causou uma exceção, tal informação pode ser colocada no objeto disparado. O tratador catch então vai conter um nome de parâmetro através do qual as infi.rmações podem ser referenciadas. 
Observação de engenharia de software 13.6 
______ Um objeto pode ser disparado sem conter informações a serem passadas; nesse caso, o mero conhecimento de que uma exceção desse tipo foi disparada pode fornecer informações suficientes para o tratador fazer seu trabalho corretamente. 
Quando uma exceção é disparada, o controle sai do bloco try corrente e continua em um tratador catch apropriado (se existir um) após aquele bloco try. E possível que o ponto de disparo esteja em um escopo profundamente aninhado dentro de um bloco try; o controle ainda seguirá para o tratador catch. E também possível que o ponto de throwpossa estar em uma chamada de função profundamente aninhada; ainda neste caso, o controle seguirá para o tratador catch. 
Um bloco try pode parecer não conter nenhuma verificação de erro e não incluir nenhum comando throw. mas o código referenciado no bloco try certamente pode fazer com que código de verificação de erro nos construtores seja exectitado. O código em um bloco try poderia executar indexação de arrays em um objeto de classe array cuja função membro operator [1 é sobrecarregada para disparar uma exceção quando ocorre um erro de subscrito fora do intervalo. Qualquer chamada de função pode invocar código que pode disparar uma exceção ou chamar 
4 outra função que dispara uma exceção. 
704 C++ COMO PROGRAMAR 
Embora uma exceção possa terminar a execução do programa, não é obrigatório fazê-lo. Porém, uma exceção 
termina o bloco em que a mesma aconteceu. 
Erro comum de programação 13.3 
Exceções deveriam ser disparadas somente dentro de um bloco try. Uma exceção disparada fora de um 
bloco try provoca uma chamada a terminate. 
Erro comum de programação 13.4 
É possível se disparar uma expressão condicional. Mas seja cuidadoso, porque as regras de promoção podem fazer com que o valor retornado pela expressão condicional seja de um tipo diferente daquele que você esperava. Por exemplo, ao disparar um int ou um double a partir da mesma expressão condicional, a expressão condicional converterá o int em um double. Então, o resultado sempre será capturado por um catch com um parâmetro double, em vez de, às vezes, capturar double (para um double realmente pretendido) e às vezes capturar int. 
13.7 Capturando uma exceção 
Os tratadores de exceção estão contidos em blocos catch. Cada bloco catch começa com a palavra-chave catch seguida por parênteses contendo um tipo (indicando o tipo de exceção que este bloco catch trata) e um nome de parâmetro opcional. Este é seguido por chaves delimitando o código de tratamento de exceções. Quando uma exceção é capturada, o código no bloco catch é executado. 
O tratador catch define seu próprio escopo. Um catch especifica entre parênteses o tipo do objeto a ser capturado. O parâmetro em um tratador catch pode ter nome ou não. Se o parâmetro tem nome, ele pode ser referenciado no tratador. Se o parâmetro não tem nome, i.e., só um tipo é listado para fins de comparação com o tipo de objeto disparado, então não são levadas informações do ponto de disparo até o tratador; só o controle passa do ponto de disparo para o tratador. Para muitas exceções, isto é aceitável. 
IErro comum de programação 13.5 
Especificar uma lista de parâmetros catch separados por vírgulas é um erro de sintaxe. 
Uma exceção cujo tipo de objeto disparado corresponde ao tipo do parâmetro no cabeçalho do catch faz com que o bloco catch. i.e., o tratador de exceção para exceções daquele tipo, seja executado. 
O tratador catch que captura uma exceção é o primeiro listado depois do bloco try atualmente ativo que corresponde ao tipo do objeto disparado. As regras de correspondência serão discutidas em breve. 
Uma exceção que não é capturada provoca uma chamada para terminate que, por default, termina o programa chamando abort. E possível se especificar um comportamento personalizado, estabelecendo que outra função seja executada, fornecendo-se o nome daquela função como parâmetro em uma chamada da função setterminate. 
Um catch seguido por parênteses incluindo reticências 
catch ( ... ) 
significa capturar todas as exceções. 
Erro comum de programação 13.6 
Colocar catch (. . .) antes de outros blocos catch impediria que aqueles blocos fossem executados; 
catch (. . .) deve ser colocado por último na lista de tratadores que segue um bloco try. 
Observação de engenharia de software 13.7 
______ Um ponto fraco na técnica de capturar exceções com catch . . . é que normalmente você não pode ter certeza de qual é o tipo da exceção. Outro ponto fraco é que, sem um parâmetro nomeado, não existe 
nenhum modo de se referir ao objeto de exceção dentro do tratador de exceção. 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 705 
É possível que nenhum tratador corresponda a um objeto disparado em particular. Isso faz com que a procura por uma correspondência continue no próximo bloco try mais externo. A medida que tal processo continua, pode eventualmente ser determinado que não existe nenhum tratador no programa que corresponda ao tipo do objeto disparado; neste caso, é chamada a função terminate, a qual, por default, chama a função abort. 
Os tratadores de exceção são pesquisados em ordem, procurando uma correspondência apropriada. O primeiro tratador que corresponde ao tipo de objeto disparado é executado. Quando aquele tratador termina de executar, o 
controle continua com o primeiro comando depois do último bloco catch, i.e., o primeiro comando depois do último tratador de exceção para aquele bloco try. 
E possível que vários tratadores de exceção representem uma correspondência aceitável para o tipo da exceção que foi disparada. Neste caso, é executado o primeiro tratador de exceção que corresponde ao tipo de exceção. Se vários tratadores apresentam uma correspondência, e se cada um destes trata a exceção diferentemente, então a ordem dos tratadores afetará a maneira como a exceção é tratada. 
E possível que vários tratadores catch possam conter um tipo de classe que corresponda ao tipo de um objeto disparado em particular. Isto pode ocorrer por várias razões. Primeiro, pode haver um tratador catch (. 
“captura tudo”, que capturará qualquer exceção. Segundo, por causa das hierarquias de herança, é possível que um objeto de uma classe derivada possa ser capturado por qualquer tratador que especifique o tipo da classe derivada ou por tratadores que especifiquem os tipos de quaisquer classes base daquela classe derivada eliminar. 
Erro comum de programação 13.7 
Colocar um catch que captura um objeto de uma classe base antes de um catch que captura um objeto de uma classe derivada daquela classe base é um erro de lógica. O catch da classe base capturaria todos os objetos de classes derivadas daquela classe base e, desse modo, o catch da classe derivada nunca seria executado. 
® Dica de teste e depura ção 13.1 
O programador determina a ordem em que os tratadores de exceção são listados. Esta ordem pode afetar C(»flO exceções originadas naquele bloco try são manipuladas. Se você estiver obtendo um comportamento inesperado no tratamento de exceções do seu programa, pode ser porque um bloco catch anterior 
está interceptando e tratando as exceções antes que possam alcançar seu tratador catch planejado. 
Às vezes, um programa pode processar muitos tipos de exceções intimamente relacionados. Em vez de fornecer classes de exceção e tratadores catch separados para cada uma, um programador pode fornecer uma única classe de exceção e um tratador catch único para um grupo de exceções. A medida que acontece cada exceção, o objeto de exceção pode ser criado com dados privados diferentes. O tratador catch pode examinar estes dados privados para identificar o tipo da exceção. 
Quando ocorre uma correspondência? O tipo de parâmetro do tratador catch corresponde ao tipo do objeto 
disparado se 
• eles são realmente do mesmo tipo. 
• o tipo de parâmetro do tratador catch é uma classe base public da classe do objeto disparado. 
• o parâmetro do tratador é de um tipo ponteiro ou referência para uma classe base e o objeto disparado é de um tipo ponteiro ou referência para uma classe derivada. 
• o tratador catch é da forma catch (. . 
Erro comum de programação 13.8 
Colocar um tratador de exceção com um tipo de argumento void* antes de tratadores de exceção com outros tipos de ponteiro provoca um erro de lógica. O tratador void* capturaria todas as exceções de tipos ponteiro, de modo que os outros tratadores nunca seriam executados. Somente catch ( . . . ) deve virapóscatch(void *) 
É necessária uma correspondência de tipo de exata. Nenhuma promoção ou conversão são executadas quando se estiver procurando um tratador de exceção, excetoconversões de classe derivada para classe base. 
706 C++ COMO PROGRAMAR 
É possível se disparar objetos const. Neste caso, o tipo de parâmetro do tratador catch deve também ser 
declarado const. 
Se nenhum tratador é encontrado para uma exceção, o programa termina. Embora isto possa parecer a coisa 
certa a ser feita, não é o que os programadores estão acostumados a fazer. Em vez disso, em geral os erros simplesmente acontecem e então a execução do programa continua, possivelmente “mancando”. 
Um bloco try seguido por vários catches se assemelha a um comando switch. Não é necessário usar break para sair um tratador de exceção de modo a passar por cima os tratadores de exceção restantes. Cada bloco catch define um escopo distinto, enquanto que todos os casos em um comando switch estão contidos dentro do escopo do switch. 
Erro comum de programação 13.9 
Colocar um ponto-e-vírgula depois de um bloco try, ou depois de qualquer tratador catch (exceto o 
último catch) em seguida a um bloco try é um erro de sintaxe. 
Um tratador de exceção não pode acessar objetos automáticos definidos dentro de seu bloco try, porque quando uma exceção ocorre o bloco try termina e todos os objetos automáticos dentro do bloco try são destruídos antes de o tratador começar a ser executado. 
O que acontece quando uma exceção ocorre em um tratador de exceção? A exceção original que foi capturada é oficialmente tratada quando o tratador de exceção começa a executar. Assim, exceções que acontecem em um tratador de exceção necessitam ser processadas fora do bloco try em que a exceção original foi disparada. 
Os tratadores de exceção podem ser escritos de vários modos. Podem dar uma olhada mais de perto em um erro e decidir chamar terminate. Podem disparar novamente uma exceção (Seção 13.8). Podem converter um tipo de exceção em outro, disparando uma exceção diferente. Podem executar qualquer recuperação necessária e retomar a execução depois do último tratador de exceção. Podem olhar para a situação que está causando o erro. remover a causa do erro e tentar novamente chamar a função original que provocou uma exceção (isto não criaria uma recursão infinita). Podem retornar algum valor de estado ao seu ambiente, etc. 
Observação de engenharia de software 13.8 
_______ É melhor incorporar sua estratégia de tratamento de exceções a um sistema desde o começo do projeto. É difícil incorporar um tratamento de exceções efetivo depois de um sistema ter sido implernentado. 
Quando um bloco try não dispara exceções e o bloco try completa normalmente a execução, o controle passa para o primeiro comando depois do último catch após o try. 
Não é possível se retornar ao ponto de disparo executando um comando return em um tratador catch. Tal 
return simplesmente retorna para a função que chamou a função que contém o bloco catch. 
Erro comum de programação comum 13.10 
Assumir que, depois que uma exceção é processada, o controle retornará ao primeiro comando depois do 
throw é um erro de lógica. 
Observação de engenharia de software 13.9 
_______ Outra razão para não usar exceções para fluxo de controle convencional é que estas “exceções adicionais” podem se confundir com exceções genuínas de tratamento de erro. Torna-se mais difícil para o programador manter o controle do número de casos de exceção. Por exemplo, quando um programa processa uma variedade excessiva de exceções, podemos realmente estar certos do que está sendo capturado por um catch ( . . . ) ? Situações excepcionais deveriam ser raras, e não comuns. 
Quando uma exceção é capturada, é possível que recursos tenham sido alocados, mas ainda não liberados no bloco try. O tratador czatch. se possível, deveria liberar estes recursos. Por exemplo, o tratador catch deveria eliminar o espaço alocado por new e fechar quaisquer arquivos abertos no bloco try que disparou a exceção. 
Um bloco catch pode processar o erro de uma maneira que possibilite ao programa continuar a executar 
corretamente. Ou o bloco catch pode terminar o programa. 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 707 
Um tratador catch pode ele mesmo descobrir um erro e disparar uma exceção. Tal exceção não será processada por tratadores catch associados ao mesmo bloco try que o tratador catch que está disparando a exceção. Em vez disso, a exceção disparada será capturada, se possível, por um tratador catch associado ao próximo bloco try mais externo. 
Erro comum de programação 13.11 
É um erro de lógica assumir que uma exceção disparada a partir de um tratador catch serci processada por aquele tratador ou por qualquer outro tratador associado ao bloco try que disparou a exceção que fez com que o tratador catch original fosse executado. 
13.8 Disparando novamente uma exceção 
É possível que o tratador que captura uma exceção decida que ele não pode processar a exceção ou que ele simplesmente queira liberar recursos antes de deixar algum outro tratá-la. Neste caso, o tratador pode simplesmente disparar novamente a exceção, com o comando 
throw; 
Tal throw sem argumentos dispara novamente a exceção. Se nenhuma exceção foi disparada, então o novo disparo provoca uma chamada para terminate. 
Erro comum de programação 13.12 
Colocar um comando throw vazio fora de um tratador catch; executar tal throw provoca uma chamada para terminate. 
Ainda que um tratador possa processar uma exceção, e independentemente de se fazer qualquer processamento sobre aquela exceção, o tratador ainda pode disparar novamente a exceção para um processamento adicional fora do tratador. 
Uma exceção disparada novamente é detectada pelo próximo bloco try externo e é tratada por um tratador de 
exceção listado depois daquele bloco try externo. 
Observação de engenharia de software 13. 10 
______ Use catch ( . . . ) para executar recuperação que não depende do tipo da exceção, tal como liberar recursos comuns. A exceção pode ser disparada novamente para alertar blocos catch externos mais 
espec(ficos. 
O programa da Fig. 13.2 demonstra o novo disparo de uma exceção. No bloco try de main. a função throwException é chamada na linha 31. No bloco try da função throwException, o comando throw na linha 17 dispara uma instância da classe exception da biblioteca padrão (definida no arquivo de cabeçalho <exception>). Esta exceção é imediatamente capturada no tratador catch na linha 19, que imprime uma mensagem de erro e então dispara novamente a exceção. Isto termina a função throwException e retorna o controle para o bloco try/catch em main. A exceção é novamente capturada na linha 34 e é impressa uma mensagem de erro. 
1 // Fig. 13.2: figl3O2.cpp 
2 II Demonstração de se disparar novamente uma exceção. 
3 #include <iostream> 
4 
5 using std: :cout; 
6 using std: :endl; 
Fig. 13.2 Disparando novamente uma exceção (parte 1 de 2). 
708 c+÷ COMO PROGRAMAR 
7 
8 #include <exception> 
9 
10 using std::exception; 
li 
12 void throwException() 
13 
14 // Dispara urna exceção e a captura imediatamente. 
15 try{ 
16 cout « “Função throwException\n’; 
17 throw exceptionO; II gera exceção 
18 
19 catch( exception e 
20 
21 cout « “Exceção tratada na função throwException\n”; 
22 throw; II dispara novamente a exceção para processamento adicional 
23 
24 
25 cout « “Isto também não deve ser impresso\n”; 
26 
27 
28 int main() 
29 
30 try{ 
31 throwException 
32 cout « “Isto não deve ser impresso\n”; 
33 
34 catch ( exception e 
35 
36 cout « “Exceção tratada em main\n”; 
37 
38 
39 cout « “Controle do programa continua após captura em main” 
40 « endl; 
41 return 0; 
42 
Função throwException 
Exceção tratada na função throwException 
Exceção tratada em main 
Controle do programa continua após captura em main 
Fig. 13.2 Disparando novamente uma exceção (parte 2 de 2). 
13.9 Especificações de exceção 
Uma especificação de exceção enumera uma lista de exceções que podem ser disparadas por uma função. 
int g( double h ) throw ( a, b, c 
II corpo da função 
É possível se restringir os tipos de exceção disparadas por uma função. Os tipos de exceção são especificados na declaração dafunção como uma especificação de exceção (também chamada de lista de throw). A especificação de exceção lista as exceções que podem ser disparadas. Uma função pode disparar as exceções indicadas ou tipos 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 709 
derivados. Apesar desta suposta garantia de que outros tipos de exceção não serão disparados, é possível se fazer isso. Se uma exceção não-listada na especificação de exceção é disparada, a função unexpected é chamada. 
Colocar throw ( ) (i.e., uma especificação de exceção vazia) depois da lista de parâmetros de uma função 
indica que a função não disparará quaisquer exceções. Tal função poderia, de fato, disparar uma exceção; isso também geraria uma chamada à função unexpected. 
Erro comum de programação 13.13 
Disparar uma exceção que não está na especificação de exceções de uma função gera uma chamada para 
unexpected. 
Uma função sem especificação de exceção pode disparar qualquer exceção. 
void gO; // esta função pode disparar qualquer exceção 
O significado da função unexpected pode ser redefinido chamando a função set_unexpected. 
Um aspecto interessante do tratamento de exceções é que o compilador não considerará como um erro de 
sintaxe se uma função contiver uma expressão throw para uma exceção não-listada na especificação de exceções da função. A função deve tentar disparar aquela exceção, durante a execução, antes de o erro ser capturado. 
Se uma função dispara uma exceção de um tipo de classe particular, aquela função também pode disparar 
exceções de todas as classes derivadas daquela classe por herança public. 
13.10 Processando exceções inesperadas 
A função unexpected chama a função especificada pela função set_unexpected. Se nenhuma função foi especificada desta maneira, terminate é chamada por default. 
A função terminate pode ser explicitamente chamada, se urna exceção disparada não pode ser capturada, se a pilha foi corrompida durante o tratamento de exceções, como a ação default para uma chamada à função unexpected e se, durante o desempilhamento da pilha iniciada por uma exceção, uma tentativa por um destruidor de disparar uma exceção faz com que terminate seja chamada. 
A função set terminate pode especificar a função que será chamada quando terminate é chamada. 
Caso contrário, terminate chama abort. 
Os protótipos para as funções set tertninate e set unexpected estão localizados no arquivo de 
cabeçalho <exception>. 
A função set terminate e a função set unexpected retornam, cada uma, um ponteiro para a última 
função chamada por terminate e unexpected. Isso possibilita ao programador salvar o ponteiro da função, de modo que ele possa ser restabelecido mais tarde. 
As funções set terininate e set unexpected aceitam ponteiros para funções como parâmetros. 
Cada parâmetro deve apontar para uma função com tipo de retorno void e sem nenhum argumento. 
Se a última ação de uma função de término definida pelo usuário não for sair de um programa, a função 
abort será automaticamente chamada para terminar a execução do programa depois que os outros comandos da função de término definida pelo usuário forem executados. 
13.11 Desempilhando a pilha 
Quando uma exceção é disparada mas não capturada em um escopo particular, a pilha (stack) de chamadas de função é desempilhada e é feita uma tentativa para capturar a exceção no próximo bloco try/catch externo. Desempilhar a pilha de chamadas de função significa que a função em que a exceção não foi capturada termina, todas as variáveis locais naquela função são destruídas e o controle retorna para o ponto em que a função foi chamada. Se aquele ponto no programa está em um bloco try. é feita uma tentativa para capturar a exceção. Se aquele ponto no programa não está em um bloco try ou a exceção não é capturada, o desempilhamento acontece novamente. Como mencionado na seção anterior, se a exceção não é capturada no programa, a função terminate é chamada para terminar o programa. O programa da Fig. 13.3 demonstra o desempilhamento da pilha. 
710 C++ COMO PROGRAMAR 
1 II Fig. 13.3: figl3O3.cpp 
2 // Demonstrando o desempilhamento da pilha. 
3 #include <iostream> 
4 
5 using std::cout; 
6 using std::endl; 
7 
8 #include <stdexcept> 
9 
10 using std: :runtime_error; 
11 
12 void function3() throw ( runtimeerror 
13 
14 throw runtimeerror( “runtimeerror em function3” ); 
15 
16 
17 void function2() throw ( runtimeerror 
18 
19 function3O; 
20 
21 
22 void functionl() throw ( runtimeerror 
23 
24 function2O; 
25 
26 
27 int main() 
28 
29 try { 
30 functionlO; 
31 
32 catch ( runtimeerror e 
33 { 
34 cout « “Ocorreu exceção: « e.what() « endi; 
35 1 
36 
37 return 0; 
38 } 
Ocorreu exceção: runtime_error em function3 
Fig. 13.3 Demonstração do desempilhamento da pilha. 
Em main, o bloco try na linha 30 chama functionl. Em seguida, functionl (definida na linha 22) chama function2. Então, function2 (definida na linha 17) chama function3. A linha 14 de function3 dispara um objeto exception. Como a linha 14 não está em um bloco try, acontece o desempilhamento da pilha - a funcao3 termina na linha 19 e o controle retoma para a function2. Como a linha 19 não está em um bloco try. acontece novamente o desempilhamento da pilha - a function2 termina na linha 24 e o controle retoma para a function. Como a linha 24 não está em um bloco try, o desempilhamento da pilha acontece mais um vez - a functionl termina na linha 30 e o controle retoma para main. Como a linha 30 está em um bloco try, a exceção pode ser capturada e processada no primeiro tratador catch correspondente depois do bloco try (na linha 32). 
13.12 Construtores, destruidores e o tratamento de exceções 
Primeiro, vamos tratar de um assunto que mencionamos, mas que ainda tem que ser satisfatoriamente resolvido. O que acontece quando um erro é descoberto em um construtor? Por exemplo, como um construtor de String 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 711 
deveria responder quando new falha e indica que foi impossibilitado de obter o espaço necessário para manter a representação interna do String? O problema é que um construtor não pode retornar um valor; assim, como comunicamos ao mundo exterior que o objeto não foi corretamente construído? Um esquema é simplesmente retornar o objeto inapropriadamente construído e esperar que alguém que use o objeto faça testes apropriados para determinar que o objeto é de fato ruim. Outro esquema é inicializar alguma variável fora do construtor. O disparo de uma exceção passa para o mundo exterior as informações sobre a falha do construtor e a responsabilidade de tratá-la. 
Para capturar uma exceção, o tratador de exceção deve ter acesso a um construtor de cópia para o objeto 
disparado (cópia default membro a membro também é válida). 
Exceções disparadas em construtores fazem com que destruidores sejam chamados para quaisquer objetos 
construídos como parte do objeto que está sendo construído antes de a exceção ser disparada. 
Destruidores são chamados para todo objeto automático construído em um bloco try antes de uma exceção ser disparada. Uma exceção é tratada no momento em que o tratador começa a ser executado; é garantido que o desempilhamento da pilha foi completado até aquele ponto. Se um destruidor invocado como resultado do desempilhamento da pilha dispara uma exceção, terminate é chamada. 
Se um objeto tem objetos membro e se uma exceção é disparada antes de o objeto externo estar completamente construído, então destruidores serão executados para os objetos membro que foram completamente construídos antes da ocorrência da exceção. 
Se um array de objetos estava parcialmente construído quando ocorreu uma exceção, só os destruidores para 
os elementos do array construídos serão chamados. 
Uma exceção poderia impedir a execução de código que normalmente liberaria um recurso, causando deste 
modo uma perda de recurso. Uma técnica para solucionar este problema é inicializar um objeto local quando o recurso é adquirido. Quando ocorrer uma exceção, o destruidor será invocado e pode liberar o recurso. 
E possívelse capturar exceções disparadas a partir de destruidores incluindo a função que chama o destruidor 
em um bloco try e fornecendo um tratador catch com o tipo apropriado. O destruidor do objeto disparado é executado depois que um tratador de exceção completa a sua execução. 
13.13 Exceções e herança 
Várias classes de exceção podem ser derivadas de uma classe base comum. Se um catch captura um ponteiro ou referência para um objeto de exceção de um tipo de uma classe base, ele também captura um ponteiro ou referência para todos os objetos de classes derivadas daquela classe base. Isso pode permitir o processamento polimórfico de erros relacionados. 
® Dica de teste e depura ção 13.2 
Usar herança com exceções possibilita a um tratador de exceção capturar erros relacionados com uma notação bastante concisa. Poderíamos certamente capturar cada tipo de ponteiro ou referência para um objeto de exceção de uma classe derivada individualmente, mas é mais conciso se capturar ponteiros ou referências para objetos de exceção da classe base. Além disso, capturar ponteiros ou referências para objetos de exceção de classes derivadas individualmente é sujeito a erros se o programador se esquecer de incluir testes explícitos para um ou mais dos tipos de ponteiro ou de referência para a classe derivada. 
13.14 Processando falhas de new 
Existem vários métodos de se lidar com falhas de new. Até este ponto, usamos a macro assert para testar o valor retornado por new. Se aquele valor for O, a macro assert termina o programa. Isto não é um mecanismo robusto para lidar com falhas de new - ele não nos permite qualquer forma de se recuperar da falha. O padrão C++ especifica que, quando new falha, é disparada uma exceção bad alloc (definida no arquivo de cabeçalho <new>). Porém, alguns compiladores podem não estar de acordo com o padrão C++ e, portanto, usam a versão de new que retorna O quando falha. Nesta seção, apresentamos três exemplos de falhas de new. O primeiro exemplo retorna O quando new falha. O segundo e terceiro exemplos usam a versão de new que dispara uma exceção badalloc quando new falha. 
712 C++ COMO PROGRAMAR 
A Fig. 13.4 demonstra new retornando O quando falha em alocar a quantidade solicitada de memória. A estrutura for na linha 12 deveria executar o laço 50 vezes e alocar um array de 5.000.000 de valores double (i.e., 40.000.000 bytes, porque um double normalmente ocupa 8 bytes) a cada passagem pelo laço. A estrutura if na linha 15 testa o resultado de cada operação new. para determinar se a memória foi alocada. Se new falha e retorna 0, a mensagem “Alocação de memória falhou’ é impressa e o laço termina. 
1 II Fig. 13.4: figl3_04.cpp 
2 // Demonstrando new retornando 0 
3 II quando a memória não é alocada 
4 #include <iostream> 
5 
6 using std::cout; 
7 
8 int main() 
9{ 
10 double *ptr[ 50 ]; 
11 
12 for ( int i = 0; i < 50; i++ 
13 ptr[ i ] = new double[ 5000000 ); 
14 
15 if ( ptr[ i ] == O ) ( // new falhou na alocação de memória 
16 cout « “Alocação de memória falhou para ptr[ 
17 « i « “ 
18 break; 
19 } 
20 else 
21 cout « “Alocados 5000000 doubles em ptr[ 
22 « i « 
23 } 
24 
25 return 0; 
26 } 
Alocados 5000000 doubles em ptr[ O 
Alocados 5000000 douiles em ptr[ 1 
Alocados 5000000 doubles em ptr[ 2 
Alocados 5000000 doubles em ptr[ 3 
Alocação de memória falhou para ptr[ 4 1 
Fig. 13.4 Demonstrando new retornando O em caso de falha. 
A saída mostra que só quatro repetições do laço foram executadas antes de new falhar e as repetições do laço terminarem. Sua saída pode ser diferente, dependendo da memória física, do espaço em disco disponível para memória virtual em seu sistema e do compilador que você usar para compilar o programa. 
___ A Fig. 13.5 demonstra new disparando bad alloc quando ela falhar em alocar a memória solicitada. A estrutura for na linha 18 dentro do bloco try deve executar 50 iterações e em cada uma alocar um array de 5.000.000 de valores double (i.e., 40.000.000 bytes, porque um double normalmente é 8 bytes). Se new falha e dispara uma exceção bad alloc, o laço termina e o programa continua no fluxo de controle de tratamento de exceções na linha 24, onde a exceção é capturada e processada. A mensagem “Ocorreu uma exceção:” é impressa, seguida pelo string (contendo a mensagem específica para a exceção “Falha na alocação”) retornado por exception . what () . A saída mostra que só quatro repetições do laço foram executadas antes de new falhar e disparar a exceção bad alloc. Sua saída pode ser diferente, dependendo da memória física, do 
espaço em disco disponível para memória virtual em seu sistema e do compilador que você usar para compilar o programa. 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 713 
1 // Fig. 13.5: figl3O5.cpp 
2 1/ Demonstrando new disparando bad_alloc 
3 II quando a memória não é alocada 
4 #include <iostreain> 
5 
6 using std: :cout; 
7 using std::endl; 
8 
9 #include <new> 
10 
11 using std: :bad_alloc; 
12 
13 int main() 
14 { 
15 double *ptr[ 50 1; 
16 
17 try{ 
18 for ( int i = 0; i < 50; i++ 
19 ptr[ i ] = new double[ 5000000 ); 
20 cout « “Alocados 5000000 doubles em ptr[ 
21 « i « 
22 } 
23 ) 
24 catch ( badalloc exception 
25 cout « Ocorreu uma exceção: 
26 « exception.what() « endl; 
27 } 
28 
29 return 0; 
30 } 
Alocados 5000000 doubles em ptr[ O 
Alocados 5000000 doubles em ptr[ 1 
Alocados 5000000 doubles em ptr[ 2 ] 
Alocados 5000000 doubles em ptr[ 3 
Ocorreu uma exceção: falha na alocação 
Fig. 13.5 Demonstrando new disparando bad_alloc em caso de falha. 
Os compiladores variam em seu suporte ao tratamento de falhas de new. Muitos compiladores de C++ retomam O por default quando new falha. Alguns destes compiladores suportam new disparando uma exceção se o arquivo de cabeçalho <new> (ou <new . h>) for incluído. Outros compiladores disparam bad alloc por default, não importando se você inclui ou não o arquivo de cabeçalho <new>. Leia a documentação de seu compilador para determinar qual o suporte oferecido por seu compilador para tratamento de falhas de new. 
O padrão C++ especifica que compiladores aderentes ao padrão podem ainda usar uma versão de new que retoma O quando ele falha. Para este finalidade, o arquivo de cabeçalho <new> define nothrow (do tipo nothrowt), que é usado como segue: 
double *ptr = new( nothrow ) double[ 5000000 ]; 
O comando precedente indica que a versão de new que não dispara exceções bad alloc (i.e., nothrow) deve ser usada para alocar um array de 5.000.000 de doubles. 
Observação de engenharia de software 13.11 
______ O padrão C+ + recomenda que, para tornar os programas mais robustos, os programadores usem a versão de new que dispara exceções bad alloc no caso de insucessos de new. 
714 C++ COMO PROGRAMAR 
Existe um recurso adicional que pode ser usado para tratamento de falhas de new. A função setnewharidler (prototipada no arquivo de cabeçalho <new>) aceita como seu parâmetro um ponteiro de função para uma função que não recebe nenhum argumento e retorna void. O ponteiro de função é registrado como a função a chamar quando new falha. Isto fornece ao programador um método uniforme de processar todas as falhas de new. não importando onde a falha acontece no programa. Uma vez que um tratador de new é registrado no programa com set new handier. new não disparará bad alloc quando fracassar. 
O operador new é na realidade um laço que tenta adquirir memória. Se a memória é alocada, new retorna um ponteiro para aquela memória. Se new falha em alocar memória e nenhuma função tratadora de new foi registrada com set new handier, new dispara uma exceção bad alloc. Se new falha na alocação de memória e uma função tratadora de new foi registrada, a função tratadora de new é chamada. O padrão C++ especifica que a função tratadora de new deveria executar uma das tarefas seguintes: 
1. Tornar disponível mais memória, apagando outra memória dinamicamente alocada, e retornar ao laço no operador new para tentar alocar a memória novamente. 
2. Disparar uma exceção do tipo badalloc. 
3. Chamar a função abort ou exit (ambas do arquivode cabeçalho <csdtlib>), para terminar o programa. 
O programa da Fig. 13.6 demonstra set new handier. A função customNewHandler simplesmente imprime uma mensagem de erro e termina o programa com uma chamada para abort. A saída mostra que só três repetições do laço foram executadas antes de new ter falhado e disparado a exceção bad alloc. Sua saída pode ser diferente, dependendo da memória física, do espaço em disco disponível para memória virtual em seu sistema e do compilador que você usar para compilar o programa. 
1 II Fig. 13.6: figl3O6.cpp 
2 // Demonstrando setnewhandler 
3 #include <iostream> 
4 
5 using std: :cout; 
6 using std: :cerr; 
7 
8 #include <new> 
9 #include <cstdlib> 
10 
11 using std: :set_new_handler; 
12 
13 void custoznNewHandler() 
14 
15 cerr « ‘customNewHandler foi chamada; 
16 abortO; 
17 
18 
19 int main() 
20{ 
21 double *ptr[ 50 ]; 
22 setnewhandler( customNewHandler ); 
23 
24 for O int i = o; i < 50; i++ 
25 ptr[ i ] = new double[ 5000000 ]; 
26 
27 cout « ‘Alocados 5000000 doubles em ptr[ 
28 « i « 
29 } 
30 
31 return 0; 
32 } 
Fig. 13.6 Demonstrando set new handier (parte 1 de 2). 
Alocados 5000000 doubles em ptr[ O 
Alocados 5000000 doubles em ptr[ 1 
Alocados 5000000 doubles em ptr[ 2 
Alocados 5000000 doubles em ptr[ 3 1 
customNewHandler foi chamada 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 715 
Fig. 13.6 Demonstrando set newhandler (parte 2 de 2). 
13.15 A classe auto_ptr e a alocação dinâmica de memória 
Uma prática comum de programação é alocar memória dinâmica (possivelmente um objeto) na memória livre, atribuir o endereço daquela memória a um ponteiro, usar o ponteiro para manipular a memória e desalocar a memória com delete quando a memória não for mais necessária. Se uma exceção acontece depois de a memória ter sido alocada e antes de o comando delete ser executado, pode ocorrer uma perda de memória. O padrão C++ fornece o gabarito de classe autoytr no arquivo de cabeçalho <memory> para lidar com esta situação. 
Um objeto da classe auto_ptr mantém um ponteiro para memória dinamicamente alocada. Quando um objeto autoytr sai do escopo, ele executa a operação delete sobre seu membro de dados ponteiro. O gabarito de classe autoytr fornece operadores * e -> de modo que um objeto auto_ptr possa ser usado como uma variável ponteiro regular. A Fig. 13.7 demonstra um objeto autoytr que aponta para um objeto da classe Integer (definida nas linhas 12 a 22). 
1 II Fig. 13.7: figl3O7.cpp 
2 II Demonstrando autoytr 
3 #include <iostream> 
4 
5 using std: :cout; 
6 using std: :endl; 
7 
8 #include <memory> 
9 
using std: :auto_ptr; 
class Integer { 
public: 
Integer( int i = O ) : value( i 
cout « “Construtor para Integer “ « value « endl; 
-Integer O 
cout « “Destruidor 
void setlnteger( int i 
int getlnteger() const 
private: 
int value; 
cout « “Criando um objeto autoytr que 
« “aponta para um Integer\n”; 
autoytr< Integer > ptrTolnteger( new Integer( 7 ) ); 
cout « “Usando o auto2tr para manipular o Integer\n”; 
ptrTolnteger->setlnteger( 99 ); 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
para Integer “ « value « endl; value = i; 
return value; 
int main() 
Fig. 13.7 Demonstrando autoytr (parte 1 de 2). 
716 C++ COMO PROGRAMAR 
33 cout « ‘Integer depois de setlnteger: 
j 34 « ( *ptrTolnteger ) .getlnteger() 
35 « “\nTerminando o programa” « endl; 
36 
37 return O; 
38 } 
Criando um objeto autoytr que aponta para um Integer 
Construtor para Integer 7 
Usando o autoytr para manipular o Integer 
Integer depois de setlnteger: 99 
Terminando o programa 
Destruidor para Integer 99 
Fig. 13.7 Demonstrando auto_ptr (parte 2 de 2). 
Alinha 29 
autoytr< Integer > ptrTolnteger( new Integer( 7 ) ); 
cria o objeto auto-ptr ptrTolnteger e inicializa-o com um ponteiro para um objeto Integer dinamicamente alocado contendo o valor 7. 
A linha 32 
ptrTolnteger->setlnteger( 99 ); 
usa o operador -> sobrecarregado de auto_ptr e o operador chamada de função ( ) para chamar a função setlnteger sobre o objeto Integer apontado por ptrTolnteger. 
A chamada 
(*ptrTolnteger ) .getlnteger() 
na linha 34 usa o operador * de autoytr sobrecarregado para derreferenciar ptrTolnteger e então usa o : operador ponto (.) e o operador chamada de função ( ) para chamar a função getlnteger sobre o objeto 
Integer apontado por ptrTolnteger. 
Como ptrTolnteger é uma variável automática local em main, ptrTolnteger é destruído quando main termina. Isto força um delete do objeto Integer apontado por ptrTolnteger que, é claro, força uma chamada para o destruidor da classe Integer. Porém, mais importante, esta técnica pode prevenir perdas de memória. 
13.16 Hierarquia de exceções da biblioteca padrão 
A experiência mostrou que exceções se enquadram bem em várias categorias. O padrão C++ inclui uma hierarquia 
de classes de exceção. Essa hierarquia é encabeçada pela classe base exception (definida no arquivo de cabeça lh <exception>) que contém a função what () que é sobrescrita em cada classe derivada, para emitir uma 
mensagem de erro apropriada. 
Da classe base exception. algumas das classes derivadas imediatas são runtime error e logicerror 
(ambas definidas no cabeçalho <stdexcept>). cada uma com várias classes derivadas. 
Também derivada de exception são as exceções disparadas por recursos da linguagem C++ - por exem plo bad alloc é disparada por new (Seção 13.14), badcast é disparada por dynamiccast (Capítulo 
21) e bad typeid é disparada por typeid (Capítulo 21). Incluindo std: :bad exception na lista de r 
throw de uma função, se uma exceção inesperada acontece, unexpected ( ) disparará bad_exception em 
vez de terminar (por default) ou em vez de chamar outra função especificada com set_unexpected. 
A classe logic error é a classe base de várias classes de exceção padrão que indicam erros de lógica em 
programas que podem freqüentemente ser evitados escrevendo-se código apropriado. As descrições de algumas 
destas classes aparecem a seguir. A classe invalid argument indica que um parâmetro inválido foi passado 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 717 
para uma função (codificação apropriada pode, é claro, evitar que parâmetros inválidos cheguem a uma função). A classe length error indica que um comprimento maior que o tamanho máximo permitido para o objeto que está sendo tratado foi usado para aquele objeto (disparamos length errors no Capítulo 19 quando lidamos com strings). A classe out of range indica que um valor tal como um subscrito para um array ou string caíram fora do intervalo válido. 
A classe runtime error é a classe base de várias outras classes de exceção padrão que indicam erros em um programa que só podem ser descobertos durante a execução. A classe overflow error indica que ocorreu um erro de ovetfiow em uma operação aritmética. A classe underflow error indica que aconteceu um erro de underflow em uma operação aritmética. 
Observação de engenharia de software 13.12 
______ A hierarquia padrão de exception se destina a servir como um ponto de partida. Os usuá rios podem disparar exceções padrão, disparar exceções derivadas das exceções padrão ou disparar suas próprias 
exceções não-derivadas das exceções padrão. 
Erro comum de programação 13.14 
Classes de exceção definidas pelo usuário não precisam ser derivadas da classe exception. Assim, não é garantido que escrever catch ( exception e ) capture todas as exceções que um programa pode encontrar 
® Dica de teste e depura ção 13.3 
Para capturar todas as exceções que podem ser disparadas em um bloco try, use catch (. . 
Resumo 
• Alguns exemplos comuns de exceções são: subscritos fora do intervalo válido em arrays, overfiow em operações aritméticas, divisão por zero, parâmetros de função inválidos e verificação de memória insuficiente para atender a um pedido de alocação por new. 
• O espírito por trás do tratamento de exceções é possibilitar aos programas capturar e tratar erros, em vez de deixá-los acontecer e simplesmente sofrer as conseqüências. Com o tratamento de exceções, se o programadornão fornece um meio de tratamento para um erro fatal, o programa terminará; erros não-fatais permitem normalmente a um programa continuar a execução, mas produzindo resultados incorretos. 
• O tratamento de exceções foi projetado para lidar com erros síncronos, i.e., erros que acontecem como resultado da execução de um programa. 
• O tratamento de exceções não foi projetado para tratar de situações assíncronas, tais como chegadas de mensagens pela rede, conclusões de EIS de disco, diques do mouse, etc.: estas são mais bem manipuladas através de outros meios. tal como o processamento de interrupções. 
• O tratamento de exceções é tipicamente usado em situações em que o erro será tratado por uma parte diferente do programa (i.e., um escopo diferente) daquela que descobriu o erro. 
• As exceções não devem ser usadas como mecanismo para especificar o fluxo de controle. O fluxo de controle com estruturas de controle convencionais é geralmente mais claro e eficiente do que com exceções. 
• O tratamento de exceções deve ser usado para processar exceções de componentes do programa que não estão preparados para tratar diretamente aquelas exceções. 
• O tratamento de exceções deve ser usado para processar exceções de componentes de software tais como funções, bibliotecas e classes que provavelmente serão amplamente usados e onde não faz sentido para aqueles componentes tratar suas próprias exceções. 
• O tratamento de exceções deveria ser usado em grandes projetos para tratar o processamento de erros de uma maneira uniforme para o projeto inteiro. 
• O tratamento de exceções em C++ é voltado para situações em que a função que descobre um erro está impossibilitada de tratá-lo. Tal função disparará uma exceção. Se a exceção corresponde ao tipo do parâmetro em um dos blocos catch. o 
código para aquele bloco catch é executado. Caso contrário, é chamada a função terminate que, por default, chama a função abort. 
• O programador inclui em um bloco try o código que pode gerar um erro que produzirá uma exceção. O bloco try é imediatamente seguido por um ou mais blocos catch. Cada bloco catch especifica o tipo de exceção que ele pode capturar e tratar. Cada bloco catch contém um tratador de exceção. 
• Quando uma exceção é disparada, o controle do programa deixa o bloco try e procura os blocos catch em busca de um tratador apropriado. Se nenhuma exceção é disparada em um bloco try, os tratadores de exceção para aquele bloco são saltados e o programa retoma a execução depois do último bloco catch. 
• Exceções são disparadas em um bloco try em uma função ou de uma função chamada direta ou indiretamente a partir do 
• Uma vez que uma exceção é disparada, o controle não pode retornar diretamente ao ponto de disparo. 
• É possível se passar informações para o tratador de exceção a partir do ponto em que ocorreu a exceção. Essas informações são o tipo de objeto disparado ou informações colocadas no objeto disparado. 
• Um tipo de exceção popular disparada é char*. É comum simplesmente se incluir uma mensagem de erro como o operando de throw. 
• As exceções disparadas por uma função particular podem ser especificadas com uma especificação de exceção. Uma especificação de exceção vazia afirma que a função não disparará quaisquer exceções. 
• As exceções são capturadas pelo tratador de exceção mais próximo (para o bloco try do qual a exceção foi disparada) especificando-se um tipo apropriado. 
• Como parte do disparo de uma exceção, uma cópia temporária do operando do throw é criada e inicializada. Este objeto temporário então inicializa a variável apropriada no tratador de exceção. O objeto temporário é destruído quando se sai do tratador de exceção. 
• Erros não são sempre verificados explicitamente. Um bloco try. por exemplo, pode parecer não conter nenhuma verificação de erro e não incluir nenhum comando throw. Mas o código referenciado no bloco try pode certamente fazer com que código de verificação de erro seja executado eliminar espaço. 
• Uma exceção termina o bloco no qual a exceção aconteceu. 
• Os tratadores de exceção estão contidos em blocos catch. Cada bloco catch começa com a palavra-chave catch seguida por parênteses contendo um tipo e um nome de parâmetro opcional. Isto é seguido por chaves delimitando o código de 
tratamento de exceção. Quando uma exceção é capturada, o código no bloco catch é executado. O tratador catch define seu próprio escopo. 
• O parâmetro em um tratador catch pode ter um nome ou não. Se o parâmetro tem um nome, o parâmetro pode ser referenciado n tratador. Se o parâmetro não tem nome, i.e., se somente um tipo é listado com a finalidade de procurar uma correspondência com o tipo de objeto disparado ou reticências para todos os tipos, então o tratador ignorará o objeto disparado. O tratador 
pode disparar novamente o objeto para um bloco try externo. 
• É possível se especificar um comportamento personalizado para substituir a função terininate. designando-se outra função a ser executada e fornecendo-se aquele nome de função como o parâmetro em uma chamada da função setterininate. 
• catch ( ) significa capturar todas as exceções. 
• É possível que nenhum tratador tenha uma correspondência com um objeto disparado particular. Isso faz com que a procura por uma correspondência continue em um bloco try externo. 
• Os tratadores de exceção são pesquisados na ordem para encontrar uma correspondência apropriada. O primeiro tratador que tem uma correspondência é executado. Quando aquele tratador termina a execução, o controle continua com o primeiro comando depois do último bloco catch. 
• A ordem dos tratadores de exceção afeta a maneira como uma exceção é tratada. 
• Um objeto de uma classe derivada pode ser capturado ou por um tratador especificando o tipo da classe derivada ou por tratadores especificando os tipos de quaisquer classes base daquela classe derivada. 
• Às vezes, um programa pode processar muitos tipos intimamente relacionados de exceções. Em vez de fornecer classes de exceção separadas e tratadores catch para cada uma, um programador pode fornecer uma classe de exceção única e um 
718 C++ COMO PROGRAMAR 
bloco try. 
CAPÍTULO 13 - TRATAMENTO DE EXCEÇÕES 719 
tratador catch para um grupo de exceções. À medida que ocorre cada exceção, o objeto de exceção pode ser criado com dados private diferentes. O tratador catch pode examinar estes dados private para identificar o tipo da exceção. 
• É possível que, muito embora esteja disponível uma correspondência precisa, uma correspondência exigindo conversões padrões será feita porque aquele tratador aparece antes daquele que resultaria em uma correspondência precisa. 
• Por default, se nenhum tratador for encontrado para uma exceção, o programa termina. 
• Um tratador de exceção não pode acessar diretamente variáveis no escopo de seu bloco try. As informações de que o tratador necessita são normalmente passadas no objeto disparado. 
• Os tratadores de exceção podem examinar mais de perto um erro e decidir chamar terminate. Podem disparar novamente uma exceção. Podem converter um tipo de exceção para outro disparando uma exceção diferente. Podem executar qualquer recuperação necessária e retomar a execução depois do último tratador de exceção. Podem examinar a situação que está causando o erro, remover a causa do erro e tentar chamar novamente a função original que causou uma exceção (isto não cria uma recursão infinita). Podem simplesmente retomar algum valor de estado para seu ambiente, etc. 
• Um tratador que captura um objeto de uma classe derivada deveria ser colocado antes de um tratador que captura um objeto da classe base. Se o tratador da classe base fosse colocado primeiro, capturaria tanto os objetos da classe base como os objetos de classes derivadas daquela classe base. 
• Quando uma exceção é capturada, é possível que recursos possam ter sido alocados, mas ainda não liberados no bloco try. O tratador catch deveria liberar estes recursos. 
• É possível que um tratador catch possa decidir que ele não pode processar a exceção.Neste caso, o tratador pode simplesmente disparar novamente a exceção. Um throw sem parâmetros dispara novamente a exceção. Se nenhuma exceção foi disparada, então o novo disparo produz uma chamada para terminate. 
• Ainda que um tratador possa processar uma exceção, e não importando se ele faz qualquer processamento daquela exceção, o tratador pode disparar novamente a exceção para processamento adicional fora do tratador. Uma exceção disparada novamente é detectada pelo próximo bloco try externo e é tratada por um tratador de exceção listado depois daquele bloco try externo. 
• Uma função sem especificação de exceção pode disparar qualquer exceção. 
• A função unexpected chama uma função especificada com a função set unexpected. Se nenhuma função foi especificada desta maneira, terminate é chamada por default. 
• A função terminate pode ser chamada de vários modos: explicitamente; se uma exceção disparada não pode ser capturada; se a pilha de chamadas de funções foi corrompido durante o tratamento de exceções; como a ação defauit em uma chamada para unexpected; ou, se durante o desempilhamento da pilha iniciada por uma exceção, uma tentativa de disparar uma exceção feita por um destruidor faz com que terminate seja chamada. 
• Os protótipos para as funções set terminate e set unexpected são encontrados no arquivo de cabeçalho <exception>. 
• As funções set terminate e set_unexpected retornam ponteiros para a última função chamada por terminate e unexpected. isto possibilita ao programador salvar o ponteiro da função de modo que ele possa ser restaurado mais tarde. 
• As funções setterminate e set unexpected aceitam ponteiros para funções como argumentos. Cada parâmetro deve apontar para uma função com tipo de retorno void e nenhum argumento. 
Se a última ação de uma função de término definida pelo usuário não é sair de um programa, a função abort será automaticamente chamada para terminar a execução do programa depois de os outros comandos da função de término definida pelo usuário terem sido executados. 
• Uma exceção disparada fora de um bloco try fará com que o programa termine. 
• Se um tratador não pode ser encontrado depois de um bloco try. o desempilhamento da pilha continua até um tratador apropriado ser encontrado. Se, em última instância, um tratador não é encontrado, então é chamada terminate. o que, por default, aborta o programa com abort. 
• Especificações de exceção listam as exceções que podem ser disparadas a partir de uma função. Uma função pode disparar as exceções indicadas ou pode disparar tipos derivados. Se uma exceção não-listada na especificação de exceção é disparada, unexpected é chamada. 
• Se uma função dispara uma exceção de um tipo de classe particular, aquela função também pode disparar exceções de todas as classes derivadas daquela classe por herança public. 
720 C++ COMO PROGRAMAR 
• Para capturaI uma exceção, o tratador de exceção deve ter acesso a um construtor de cópia para o objeto disparado. 
• As exceções disparadas a partir de construtores fazem com que sejam chamados destruidores para todos os objetos de classes base completados e os objetos membros do objeto que estava sendo construído antes de a exceção ter sido disparada. 
• Se um array de objetos havia sido parcialmente construído quando ocorreu uma exceção, só os destruidores para os elementos completamente construídos do array serão chamados. 
• As exceções disparadas a partir de destruidores podem ser capturadas incluindo-se a função que chama o destruidor em um bloco try e fornecendo-se um tratador catch com o tipo apropriado. 
• Uma razão poderosa para usar herança com exceções é criar a possibilidade de capturar facilmente uma variedade de erros relacionados, com uma notação concisa. Poderíamos certamente capturar cada tipo de objeto de exceção de uma classe 
derivada individualmente, mas, se todas as exceções derivadas são tratadas da mesma forma, é muito mais conciso simplesmente se capturar o objeto de exceção da classe base. 
• O padrão C++ especifica que, quando new falha, ela dispara uma exceção bad alloc (bad alloc é definido no arquivo de cabeçalho <new>). 
• Alguns compiladores não estão atualizados de acordo com o padrão C++ e ainda usam a versão de new que retorna O quando falha. 
• A função set new handier (prototipada no arquivo de cabeçalho <new>) aceita como seu argumento um ponteiro de função para uma função que não aceita nenhum argumento e retorna void. O ponteiro de função é registrado como a função 
a ser chamada quando new falha. Uma vez que um tratador de new é registrado com set_riew_haridler, new não disparará bad alba quando ocorrer uma falha. 
• Um objeto da classe autoytr mantém um ponteiro para memória alocada dinamicamente. Quando um objeto autoytr sai do escopo, ele executa automaticamente uma operação delete sobre seu membro de dados ponteiro. O gabarito de 
classe autoytr fornece operadores * e -> de modo que um objeto auto_ptr pode ser usado como uma variável ponteiro regular. 
• O padrão C++ inclui uma hierarquia de classes de exceção encabeçadas pela classe base exception (definida no arquivo cabeçalho <exception>), que oferece o serviço what () que é redefinido em cada classe derivada para emitir uma mensagem de erro apropriada. 
• Incluindo-se std: badexception na lista de throw de uma definição de função, se uma exceção inesperada ocorrer, unexpected () disparará bad exception em vez de terminar (por default) ou em vez de chamar outra função especificada com set_unexpected. 
Terminologia 
abort O disparar expressão 
aplicativo para missões críticas disparar novamente uma exceção 
argumento de catch disparar uma exceção 
arquivo de cabeçalho <exception> disparar uma exceção inesperada 
arquivo de cabeçalho <memory> dynamic_cast 
arquivo de cabeçalho <new> especificação de exceção 
arquivo de cabeçalho <stdexcept> especificação de exceção vazia 
autoptr especificação de throw vazia 
bad_alloc exceção 
badcast exceção não-capturada 
badtypeid exit() 
bloco catch função sem especificação de exceção 
bloco try invalid_argument 
bloco try externo lengtherror 
capturar um grupo de exceções lista de disparo 
capturar uma exceção lista de exceções 
catch(...) bogicerror 
catch(void *) macroassert 
condição excepcional new_handler 
declaração de exceção nothrow 
desempilhamento da pilha objeto de exceção 
722 C++ COMO PROGRAMAR 
Dicas de desempenho 
13.1 Embora seja possível se usar o tratamento de exceções para propósitos diferentes do tratamento de erros, isso pode reduzir o desempenho do programa. 
13.2 O tratamento de exceções é geralmente implementado em compiladores de tal maneira que quando não ocorrem exceções, pouco ou nenhum os’erhead é imposto pela presença do código de tratamento de exceções. Quando ocorrem exceções, elas incorrem em overhead durante a execução. Certamente, a presença de código de tratamento de exceções faz o programa consumir mais memória. 
Observações de engenharia de software 
13.1 O fluxo de controle com estruturas de controle convencionais é geralmente mais claro e eficiente do que com exceções. 
13.2 O tratamento de exceções é bem adequado para sistemas com componentes desenvolvidos separadamente. O tratamento de exceções facilita a combinação dos componentes. Cada componente pode executar sua própria detecção de exceção, 
separada do tratamento das exceções em outro escopo. 
13.3 Ao lidar com bibliotecas, o chamador da função de biblioteca provavelmente terá em mente um processamento de erro específico para uma exceção gerada na função de biblioteca. E improvável que uma função de biblioteca execute um processamento de erro que satisfaça às necessidades particulares de todos os usuários. Portanto, exceções são meios apropriados para tratar erros produzidos por funções de bibliotecas. 
13.4 Uma idéia-chave do tratamento de exceções é que a parte de um programa ou sistema que tratará a exceção pode ser bastante diferente ou distante da parte do programa que descobriu e gerou a situação excepcional. 
13.5 Se é necessário passar informações

Outros materiais