PUCRS
Programação de Software Básico
Faculdade de Informática


Exceções em C++


O tratamento de exceções permite capturar erros ocorridos durante a execução de um programa. Para tanto, um programa pode 'lançar' e 'capturar' exceções.

Para lançar uma exceção um objeto de uma classe 
(escolhida pelo programador) deve ser instanciado através do comando throw (xxxx). No lugar do parâmetro 'xxxx' deve ser colocada a construtora de uma classe escolhida pelo programador para representar a exceção.  Nesta construtora podem ser passados parâmetros de forma que no momento da 'captura'  da exceção se tenha dados sobre a mesma.

O comando
throw (xxxx) deve ser utilizado somente dentro de um bloco try {  }.  

Ao lançar uma exceção o fluxo de execução do programa é desviado para o primeiro comando após o final do bloco try {  }.  Este comando deve que deve ser um catch(
xxxx).

Do ponto de vista da linguagem C++, par
try-catch formam um único comando.

O
comando catch(xxxx) é usado para capturar uma exceção, o parâmetro 'xxxx' especifica qual o tipo de exceção será tratado neste bloco.  Este tipo é identificado pelo nome da classe que foi usada no momento do lançamento da exceção com o comando throw.

No exemplo a seguir, o programa gera uma exceção caso haja a tentativa de divisão por zero. A classe TrataDivZero define o tipo da exceção. Neste programa, ao lançar uma exceção ocorre o instanciamento de um objeto da classe 
TrataDivZero e é passado à construtora desta classe um inteiro informando o número da linha, no programa-fonte, onde a exceçao ocorreu.

Após a execução do tratador de exceção o fluxo do programa segue para o primeiro comando após o bloco do comando catch().

// ************************************************
//  Tratamento de Exceções
//  Excecoes2.cpp
// ************************************************
#include <iostream>

using std::cout;
using std::cin;
using std::endl;
using std::string;

class TrataDivZero
{
      public:
            TrataDivZero(int n);           
            void Msg();
      private:
              int NLinha;
};
TrataDivZero::TrataDivZero(int n)
{
     NLinha = n;
}     

void TrataDivZero::Msg()
{
      cout << "Tratador de erro foi chamado ..." << endl;
      cout << "Erro na linha: " << NLinha << endl;    
}     

int main()
{
    
     int n, m;
     cout << "Numerador: ";
     cin >> n;
     cout << "Denominador: ";
     cin >> m;
   
     try // inicio do bloco TRY
     {
       
        if (m==0)
        { 
             // lança a exceção, criando um objeto da classe TrataDivZero
             // e passando o número da linha onde o erro ocorreu
             throw (TrataDivZero(__LINE__)); 
             // dentro deste bloco nada é executado após o throw
        }
        cout << "Divisão: "  << (double)n/m << endl;
     } // final do bloco TRY
     
     catch (TrataDivZero T) // pode ter ou não o objeto
     {                      // apenas o tipo é obrigatório
          T.Msg();
     }
     
     cout << "Fim..." << endl;
     system("pause");
     return 0;
}



Exceção não Tratadas

Caso uma exceção seja lançada e não haja um catch para tratá-la, o programa encerra de forma anormal, exibindo uma mensagem de erro.

No código acima, por exemplo, há somente um tratador para a exceção que lança um objeto da classe TrataDivZero, assim, se for lançada uma exceção com um objeto string, usando o comando
            throw (string("aaa"));
o programa irá encerrar com uma mensagem de erro semelhante a que segue:

C:\MyDocuments\Doc\PRGSWB\Testes>TestesCPP
Numerador: 8
Denominador: 0

This application has requested the Runtime to terminate it in an unusua
Please contact the application's support team for more information.

C:\MyDocuments\Doc\PRGSWB\Testes>


Inserindo vários tratadores de exceção

É possível e muito comum, que um programa tenha vários tratadores de exceção (catch) após um bloco try { }.  

Note que para cada tipo de exceção pode haver um tratador. Se houver mais de uma, apenas a primeira será executada.

No exemplo abaixo, no bloco try são lançados 3 tipos de exceção:
Se ocorrer o lançamento de uma excção nõa prevista pelos 'catch' a mesmo pode ser pega pelo tratador genérico catch (...).

int main()
{
    
     int n, m;
     cout << "Numerador: ";
     cin >> n;
     cout << "Denominador: ";
     cin >> m;
   
     try
     {
       
        if (m==0)
        {              
             throw (TrataDivZero(__LINE__));  
             cout << "Problemas...."  ;   // esta linha nunca é executada    
        }
        if (m<0)
        {
            string temp = "Problema com Raiz Quadrada de nro negativo.";              
            throw (string(temp)); // LANÇA UMA EXCEÇÃO COM UMA string    
        }
        if (n<0)
        {
            throw (10); // LANÇA UMA EXCEÇÃO COM UM int   
        }
       
        cout << "Divisão: "  << (double)n/m << endl;
        cout << "Raiz Quadrada: " << sqrt(m) << endl;
     }
     catch (TrataDivZero T) // pode ter ou não o objeto
     {                      // apenas o tipo é obrigatório
          T.Msg();
     }
     catch (string S) // pode ter ou não o objeto
     {                // apenas o tipo é obrigatório
          cout << "Tratador de exceção de string" << endl;           
          cout << S << endl;
     }
     catch (...)
     {
            cout << "Tratador de exceção GENERICO !!" << endl;         
     }
     cout << "Fim..." << endl;
     system("pause");
     return 0;
}


Exceções Padronizadas 

As exceções padronizadas permitem capturar  algumas exceções geradas por comandos ou funções da biblioteca de C++.  A mais comum delas é a bad_alloc que infforma que faltou memória para executar o comando new. Para utilizá-la o programa deve incluir as diretivas

 
#include <exception>
e
using std::bad_alloc;


#include <iostream>
#include <exception>
using std::cout;
using std::endl;
using std::bad_alloc;

int main()
{
    
    double *ptr;
     try
     {
        while (1)
        {
              cout << "Tentando Alocar...\n";
              ptr = new double[500000];
        }
       
     }
     catch (bad_alloc E)
     {   
          cout << "Faltou Memoria...\n" << endl;     
       
     } // aqui o objeto T não existe mais !!
     for(int i=0; i< 100;i++)
     cout << "Fim..." << endl;
    
     return 0;
}



Handlers de Exceções

Além dos tratadores de exceção a linguagem C++ possui funções de tratamento de exceções ou handlers, que são funções do próprio programa para tratar exceções padronizadas.
Entre estas exceções destacam-se:
Para tratar o encerramento prematuro de um programa causado pelo não tratamento de uma exceção, existe o Handler terminate, que pode ser setado ela função set_terminate(Funcao_De_Tratamento). Para usá-la é necessário incluir o comando  using std::set_terminate no início do fonte.

Para tratar a falta de memória na execução de um comando new, existe o Handle
new_handler, que pode ser setado ela função set_new_handler(Funcao_De_Tratamento). Para usá-la é necessário incluir o comando  using std::set_new_handler no início do fonte.

Para tratar a falta de tratamento de uma exceção, existe o Handle unexpected, que pode ser setado ela função set_unexpected(Funcao_De_Tratamento). Para usá-la é necessário incluir o comando  using std::set_unexpected no início do fonte.

Atenção: A handler de falha no new 
tem prioridade sobre todas as demais, ou seja, se ela existir, somente ela será executada.  A handler de encerramento tem priridade sobre a handler de exceção inesperada, ou seja se ela existir, a handler exceção de inesperada não será executada.

No exemplo abaixo, pode observar o uso destes três Handlers.


#include <iostream>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::set_terminate;
using std::set_unexpected;
using std::set_new_handler;

using std::bad_alloc;

void Encerra()
{
     cout << "Função Alternativa para encerramento..." << endl;   
     system ("pause");
}
void ExcecaoInesperada()
{
     cout << "Execeção Inesperada..." << endl;    
     system ("pause");
}

void TrataFalhaNoNew()
{
     cout << "O 'new' falhou..." << endl;    
     system ("pause");

}
int main()
{
    
    double *ptr;
     set_unexpected(ExcecaoInesperada); // trata exeção inesperada
    
     set_new_handler(TrataFalhaNoNew); // Isto impede o lançamento da
                                       // exceção bad_alloc, na falha do new 

     set_terminate(Encerra); // se ela existe, somente ela é executada
                                      
     try
     {
        //throw(6); // provoca a execução da função
       
        while (1)
        {
              cout << "Alocando...\n";
              ptr = new double[500000];
        }
       
     }
     catch(float) // trata apenas exceções de float
     {
     }

     cout << "Fim..." << endl;
     system("pause");
     return 0;
}



Outros Exemplos

Exemplo 1
Exemplo 2



FIM.