Tratamento de Exceções

Ver Todos os Exercícios

Teoria

Introdução

Eai, tudo bem?

Eu tenho certeza que você já se deparou com a situação de rodar um programa e ele encerrar com retorno de um valor diferente de 0. Isso acontece pois o 0 como retorno ao final do programa é uma forma de indicar que não ocorreu nenhum erro durante a sua execução. Qualquer valor diferente de 0 indica que ocorreu uma exceção, ou seja, houve algum erro que não foi tratado.

Mas como fazemos para que isso não ocorra? No C++, é possível tratarmos essas exceções sem que o nosso programa pare de funcionar. Ficou curioso sobre como isso funciona? Então bora lá!

try, catch e trow

Inicialmente, vamos trabalhar em cima de um exemplo simples. Você, provavelmente, quando estava aprendendo sobre funções, já fez uma função bem simples para realizar alguma operação matemática. Vamos retornar um pouco para a nossa origem e vamos criar uma função chamada d i v i d i r . Essa função recebe dois números do tipo d o u b l e e retorna a divisão entre eles:

Se nós colocarmos o seguinte código para rodar, nós não vamos encontrar problema algum:

Esse código nos deu a seguinte saída:

Mas o que aconteceria se, dentro da nossa função m a i n , nós tentássemos escrever o seguinte código:

Nesse caso, estamos tentando realizar uma divisão por 0 , e isso claramente vai dar um erro no nosso programa. Para evitar que isso ocorra, podemos fazer uma rotina para tratar essa exceção. Primeiramente, vamos escrever a rotina que querermos que seja executada dentro de um bloco de t r y . Ao utilizar esse bloco, estamos indicando que existe a possibilidade de ocorrer um erro na rotina que escrevemos dentro dele.

Vamos tentar entender o que está acontecendo dentro da rotina t r y . Ao chamarmos a função d i v i s a o , ele vai acessar o código onde escrever nós escrevermos a função e vai passar os valores 1 e 0 como os parâmetros d i v i d e n d o e d i v i s o r . O problema ocorre quando tentamos retornar o valor da divisão, logo, seria interessante que acrescentássemos alguma forma dele identificar o erro dentro dessa função. Para isso, podemos utilizar o t h r o w :

Esse t h o w é uma forma de indicarmos um fim para rotina t r y . Ele serve de forma similar a um r e t u r n , pois ele vai enviar o que está depois dele, que no caso do nosso código é a mensagem “Tentativa de Divisao por 0”, para uma outra rotina. Essa rotina que vai receber isso como argumento, é a c a t c h . Ela vem depois da t r y e é a rotina que será executado somente no caso de algum erro ser detectado dentro da rotina t r y .

No nosso caso, vamos colocar para que uma mensagem seja impressa na tela:

O c a t c h recebe como argumento aquilo que é enviado para o pelo t h r o w , como estamos enviando uma frase, será recebido um ponteiro o tipo c o n s t c h a r .

Com isso, ficamos com seguinte código:

O que vai nos dar a seguinte saída:

Múltiplas exceções e tratamento genérico

Agora, vamos supor que temos uma rotina na qual é possível encontrarmos diferentes exceções com diferentes tipos. Para isso, podemos fazer múltiplas rotinas c a t c h , cada uma tratando uma exceção lançada por um t h r o w .

Nesse caso, a rotina c a t c h iniciada na linha 14 seria realizada após no caso de algum t h r o w enviar um inteiro. Como a exceção do nosso código envia um c o n s t   c h a r * , essa rotina não será realizada pelo código.

Existe ainda uma maneira de utilizarmos uma rotina c a t c h para filtrarmos qualquer tipo de exceção lançada. Para isso, basta utilizarmos c a t c h ( … ) , que funciona para qualquer tipo de exceção. O problema de utilizarmos esse tipo de c a t c h é que criamos um tratamento que não é único para cada exceção, ou seja, não elaboramos uma resposta personalizada para a nossa exceção, mas sim uma genérica para toda possível exceção.

Nesse caso, o erro será tratado pela rotina c a t c h ( … ) , o que vai nos dar a seguinte saída:

Perceba que, nesse caso, nós perdemos a informação enviada pelo t h r o w .

Criando uma classe para as exceções

Uma maneira de deixarmos esse tratamento de exceções algo mais profissional e bem elaborado no nosso código é criarmos uma classe para tratar os erros do nosso código.

Para isso, vamos criar uma classe chamada T r a t a r E x c e c a o . Dentro dela, vamos ter o parâmetro privado m e n s a g e m e como membros públicos, teremos um construtor e um método g e t M e n s a g e m .

No construtor, vamos passar o parâmetro m s g para o atributo m e n s a g e m , dessa forma, fazemos com que o nosso construtor funcione para instanciar um objeto que já tem o seu parâmetro definido.

O método g e t M e n s a g e m deve retornar o valor salvo no parâmetro m e n s a g e m :

Agora que já possuímos a nossa classe T r a t a r E x c e c a o concluída, podemos utilizá-la para tratar a exceção da divisão por 0 . Para que isso funcione, vamos modificar a nossa função divisão e fazer com o t h r o w envie um objeto da nossa classe.

Vamos fazer o nosso bloco de c a t c h receber um objeto da classe T r a t a r E x c e c a o e, na sua rotina, imprimir o valor do parâmetro m e n s a g e m .

Por fim, o nosso código ficou assim:

E teremos a seguinte saída:

Como já possuímos noção de como tratar as possíveis exceções do nosso código com t h r o w , t r y e c a t c h , que tal exercitarmos esses nossos novos conhecimentos com algumas questões de fixação? Então cola lá que eu estou te esperando!

Múltiplas exceções e tratamento genérico

Criando uma classe para as exceções

Exercícios Resolvidos

Exercício Resolvido #1

Explique como uma exceção é tratada usando t h r o w , t r y e c a t c h .

Passo 1

Eai, tudo beleza?

Quando existe a chance de um código nosso gerar uma exceção, nós o colocamos dentro de um bloco de t r y . Nesse bloco, no caso onde pode ocorrer um erro, nós utilizamos o t h r o w , que encerra a rotina que está sendo executada no t r y e envia um dado como argumento para o próximo bloco, o c a t c h . Esse bloco recebe como parâmetro o que é enviado pelo t h r o w inicia uma nova rotina, própria para o tratamento daquela exceção.

Resposta

Ei, a resposta está no passo a passo :)

Exercício Resolvido #2

Explique o que acontece ao utilizar o c a t c h ( … ) ao invés de especificar o que ele está recebendo como parâmetro.

Passo 1

Eai, tudo tranquilo?

Quando vamos tratar de exceções, precisamos enviá-las para um bloco de c a t c h . Esse bloco recebe um parâmetro cujo tipo deve ser especificado no t h r o w . Mas existe a situação de termos alguma exceção cujo tipo não está como parâmetro em nenhum c a t c h . Nesse caso, podemos utilizar o c a t c h do tipo d e f a u l t .

Esse c a t c h vai pegar e tratar qualquer exceção enviada por t h r o w independente do tipo de dado. Ou seja, vamos ter um tratamento genérico para as exceções que não possuem um c a t c h específico para o seu tipo.

Resposta

Ei, a resposta está no passo a passo :)

Exercício Resolvido #3

Escreva um m a i n genérico que consiga capturar qualquer tipo de exceção.

Passo 1

Eai, tudo beleza?

A estrutura geral para tratamento de exceções dentro de um código envolve o uso de três comandos principais: t r y , c a t c h e t h r o w . Pra resolver a questão, precisamos indicar onde cada uma delas entra num código genérico.

Bora lá!

Passo 2

Para tratarmos uma exceção dentro da nossa função m a i n , precisamos, inicialmente, que a rotina passiva de erros esteja dentro de um bloco de t r y :

Passo 3

Em seguida, vamos fazer uma rotina de c a t c h genérica, ou seja, vamos utilizar o c a t c h ( … ) para que qualquer tipo de exceção possa ser tratada por essa rotina:

É importante ressaltarmos a necessidade de um t h r o w dentro do bloco t r y para cada possível erro, pois é isso que permite o envio da exceção para o bloco t r y .

Resposta

Exercício Resolvido #4

Encontre o erro na seguinte estrutura de tratamento de erro:

Passo 1

Eai, tudo beleza?

Inicialmente, vamos entender o código que nos foi passado. Nele há uma função d i v i s a o . Vamos analisar o que essa função faz. Ela inicia checando se o valor b é nulo, caso ele seja, ela aciona um t h r o w com a mensagem “Tentativa de dividir por 0”. Caso não seja, ela vai retornar a divisão entre os valores passados como argumentos.

Na função m a i n , existe um bloco de t r y e, dentro desse bloco, há a chamada da função d i v i s a o com a passagem dos valores 1 e 0 como argumentos. Após esse bloco, há um bloco de c a t c h , que recebe um inteiro i como argumento e imprime uma mensagem de erro.

Passo 2

Agora, vamos analisar o que esse programa está fazendo de fato. Ao enviar os valores 1 e 0 como argumentos para a função d i v i d i r , o parâmetro a vai assumir o valor 1 enquanto o b vai assumir o valor 0 . Dentro da função, vai haver a análise do valor de b e o t h r o w será acionado.

Após um t h r o w ser acionado, é necessário que uma a exceção seja capturada por um bloco de c a t c h . No entanto, esse t h r o w está enviando um dado do tipo c o n s t   c h a r * , enquanto o único c a t c h do código é feito para receber um inteiro, ou seja, esse c a t c h não vai capturar a exceção enviada!

Para que isso funcionasse, seria necessário que esse c a t c h recebesse um c o n s t   c h a r * ou que o t h r o w enviasse um inteiro.

Resposta

Ei, a resposta está no passo a passo :)

Exercício Resolvido #5

No código abaixo, há um problema ao tentarmos rodar o código na m a i n ( ) .

Identifique esse problema e crie uma rotina para tratá-lo.

Passo 1

Eai, tudo beleza?

Primeiramente, vamos entender esse código. Ele possui uma função chamada M e d i a H a r m o n i c a . A partir da linha 19, nós podemos ver que essa função retorna a média harmônica dos valores que ela recebe.

Dentro da função m a i n , são declarados dois valores: x = 3.5 e y =   - 3.5 . Logo em seguida é impressa uma mensagem na tela indicando que será impresso o valor da média harmônica dessas duas variáveis e é chamada a função M e d i a H a r m o n i c a que faz a conta.

Passo 2

O problema desse código é que os valores passados como argumentos da função resultam numa operação de divisão onde o denominador é nulo:

2 * a * b a + b = 2 * 3.5 * - 3.5 3.5 - 3.5 = - 24,5 0

Essa conta vai gerar um erro no nosso código. Para que isso não ocorra, podemos tratar esse erro de divisão no nosso código.

Para isso, vamos colocar um t h r o w dentro da função M e d i a H a r m o n i c a para o caso de a + b = 0 :

Com isso, quando o código encontrar a = - b , uma mensagem de erro indicando que os valores são inválidos para o cálculo vai ser enviada.

Agora precisamos escrever o restante do código que vai pegar essa mensagem de erro e exibir pro usuário.

Passo 3

Agora, dentro da nossa m a i n , vamos colocar o código para imprimir a mensagem dentro de uma rotina de t r y :

Aqui, estamos indicando pro C++ qual parte do código pode contar um erro.

Por fim, vamos acrescentar uma rotina de c a t c h para tratar a exceção enviada de forma a imprimir na tela que ocorreu um erro:

Prontinho!

Resposta

Exercício Resolvido #6

Implemente uma classe própria que gerencie exceções.

Passo 1

Eai, tudo beleza?

Vamos começar criando declarando a nossa classe, que chamaremos de E x c e c a o . Nela, vamos ter uma como membro privado um atributo m s g do tipo s t r i n g , para salvarmos uma mensagem referente à exceção ocorrida. Como membros públicos, vamos tem um construtor e um método do tipo v o i d para imprimir o valor salvo em m s g na tela.

Passo 2

Vamos escrever o nosso contrutor. A função dele vai ser, basicamente, construir um objeto da classe e x c e c a o . Para que esse objeto seja funcional para nós, basta que o valor do seu atributo mensagem esteja preenchido. Logo, vamos apenas passar o valor recebido pelo construtor para o parâmetro m s g .

Passo 3

Por fim, vamos definir o método m e n s a g e m . Nele, vamos fazer com que o valor salvo em m s g seja impresso na tela:

Dessa forma, criamos uma classe que pode ser utilizada para o tratar exceções. Para isso, bastaria que o t h r o w enviasse um objeto dessa classe para que ele seja capturado pelo c a t c h e, dentro dessa rotina, chamássemos o método m e n s a g e m para imprimir a mensagem do erro.

Resposta

Exercício Resolvido #7

Crie uma classe chamada O p e r a t i o n E x c e p t i o n para tratar erros de operações matemáticas. Faça uma função chamada R a i z Q u a d r a d a que recebe um número real e retorna a sua raiz. Em caso de erro, essa função deve utilizar a classe chamada para tratar o erro.

Passo 1

Eai, tudo beleza?

Nessa questão, nós vamos fazer uma classe para tratar erros de operações matemáticas e vamos fazer uma função que, em caso de erro, instancie um objeto dessa classe para tratar esse erro.

Vamos iniciar o nosso código incluindo as bibliotecas necessárias, inclusive a biblioteca c m a t h para operações matemáticas. Além disso, vamos criar a nossa classe com o parâmetro m s g , um construtor e um método i m p r i m i r M e n s a g e m .

Passo 2

Vamos definir o construtor para essa classe. Nele, vamos passar o valor enviado como parâmetro para o atributo mensagem.

Agora, vamos definir o método i m p r i m i r M e n s a g e m . Nele, nós vamos pegar o valor que está armazenado na variável m s g e vamos imprimi-lo na tela depois de " O p e r a t i o n   E x c e p t i o n :   " para indicarmos que o que está acontecendo é uma exceção tratada pela classe O p e r a t i o n E x c e p t i o n .

Passo 3

Agora, fora da classe, vamos criar a função R a i z Q u a d r a d a . Ela deve receber um valor do tipo d o u b l e e retornar a sua raiz quadrada usando a função s q r t da biblioteca c m a t h . Antes dela retornar um valor, vamos checar se o número passado é negativo, pois, nesse caso, vamos ter um erro ao tentar calcular uma raiz quadrada de um número negativo. Caso esse valor seja negativo, vamos usar o t h r o w para lançar um erro do tipo O p e r a t i o n E x c e p t i o n e, dentro do construtor, vamos enviar a mensagem para especificar o tipo de erro.

Resposta

Exercício Resolvido #8

Desenvolva uma função m a i n para um uso da classe e da função da questão anterior. Faça em um arquivo .cpp diferente do anterior.

Passo 1

Eai, tudo beleza?

Nessa questão, nós vamos utilizar o código da questão anterior com a classe O p e r a t i o n E x c e p t i o n e o a função R a i z Q u a d r a d a . Vamos salvar o arquivo da aula passada com o nome O p e r a t i o n E x c e p t i o n . c p p e incluir ele no nosso novo arquivo de código:

Passo 2

Agora, vamos iniciar a função m a i n . Nela, vamos fazer um bloco de t r y , no qual nós vamos tentar imprimir na tela a raiz quadrada de um número negativo usando a função R a i z Q u a d r a d a .

Passo 3

Para garantirmos que a exceção será tratada, vamos fazer um c a t c h que recebe um objeto da classe O p e r a t i o n E x c e p t i o n . Dentro desse bloco, nós vamos chamar o método i m p r i m i r M e n s a g e m ( ) e, com isso, podemos encerrar a nossa m a i n .

Resposta