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:
- uma com um objeto da classe TrataDivZero;
- uma com um objeto da classe string;
- uma com um int.
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:
- a exceção de encerramento;
- a exceção de falta de memória para o comando new;
- a exceção de falta de tratamento de uma exceção lançada.
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.