Controlo de Excepções
class X
{
public:
X(const X&) {
X() {
virtual ~X() {
cout << "copy constructor\n";
cout << "constructor\n"; }
cout << ”destructor\n";
}
};
void f(X&){}
void ff(void) {
X x;
cout << "f (call)" << endl;
f(x);
cout << "f (return)" << endl;
}
constructor
f (call)
f (return)
destructor
}
void ff(void) {
X x;
cout << "before throw" << endl;
throw x;
cout << "after throw" << endl;
}
void fff(void)
{
try {
ff();
}
catch(...) { cout << "catch\n"; }
}
int main(int argc, char* argv[])
{
fff();
return 0;
}
constructor
before throw
copy constructor
destructor
catch
destructor
void ff(void) {
X x;
cout << "before throw" << endl;
x;
throw &x;
cout << "after throw" << endl;
}
void fff(void)
{
try {
ff();
}
catch(...) { cout << "catch\n"; }
}
int main(int argc, char* argv[])
{
fff();
return 0;
}
constructor
before throw
destructor
catch
void ff(void) {
X x;
cout << "before throw" << endl;
throw x;
cout << "after throw" << endl;
}
void fff(void)
{
try {
ff();
}
catch(X
x) { cout << "catch\n"; }
catch(...)
}
int main(int argc, char* argv[])
{
fff();
return 0;
}
constructor
before throw
copy constructor
copy constructor
destructor
catch
destructor
destructor
void ff(void) {
static X
X x;
x;
cout << "before throw" << endl;
x;
throw &x;
cout << "after throw" << endl;
}
void fff(void)
{
try {
ff();
}
catch(X*
x) { cout << "catch\n"; }
catch(...)
}
int main(int argc, char* argv[])
{
fff();
return 0;
}
constructor
before throw
destructor
catch
void ff(void) {
X x;
cout << "before throw" << endl;
throw x;
cout << "after throw" << endl;
}
void fff(void)
{
try {
ff();
}
catch(...)
catch(X&
x) { cout << "catch\n"; }
static X x;
constructor
before throw
copy constructor
destructor
catch
destructor
}
int main(int argc, char* argv[])
{
fff();
return 0;
}
slicing problem
class X{ ... };
void f()
{
static X x; // global
................
throw &x;
//ponteiro
}
void ff()
{
try
{
f();
}
catch(X *x) { ... } // ponteiro
}
class X
{
public:
vitrual void f() throw();
};
class Y : public X { ... };
class Z : public Y
{
public:
vitrual void f() throw();
.....................
};
void ff()
{
...............
if( ... ) throw Z();
}
slicing problem
void fff()
{
try
{
ff(); }
catch (X x) {
cerr << x.f(); }
}
// chama X::f(), nunca Z::f()
slicing problem
static X x;
void ff()
{
..............
if( ... ) throw Z();
}
void fff()
{
try { ff(); }
catch (X& x) {
cerr << x.f(); }
}
// agora chama Z::f()
void f(int i)
{
try
{
if( . . . )
}
}
catch(float f)
{
........
}
}
throw i;
extern void f();
void ff() throw (int);
void ff() throw();
void f() throw(X,Y) { ... } - específica a lista (X,Y) das excepções
que podem ser geradas pela função f. Se a função f gerar outras
excepções (que não são X nem Y), a função especial que se chama
unexpected vai ser chamada automaticamente.
Por defeito a função unexpected chama a função terminate.
Podemos redefinir estas funções usando funções da biblioteca tais
como set_terminate e set_unexpected.
A função terminate é chamada nas seguintes ocasiões:
1. É impossível encontrar o respectivo bloco catch.
2. Existem erros na pilha.
3. Existem erros no destrutor que destroí os objectos
automáticos (locais).
Sumário de excepções
1. O mecanismo de controlo de excepções permite interromper o
programa em pontos onde aparece uma situação anormal. Mais
frequentemente este mecanismo é usado para procurar os erros.
2. Quando o programa é interrompido o controlo, e possivelmente
os dados ,vão ser passados do ponto de execução ao ponto de um
programa predefinido onde a excepção vai ser tratada.
3. Este mecanismo pode ser usado só para as excepções
sincronizadas. Isto significa que estas excepções só podem ser
geradas dentro do programa. Por exemplo, não é possível considerar
excepções que vão ser geradas ao pressionar as teclas Ctrl-C.
4. Para o controlo das excepções a linguagem C++ introduz três
novas palavras chaves que são throw, try e catch.
5. As excepções podem ser de qualquer tipo mas mais
frequentemente têm o tipo class ou struct.
6. O bloco do código que pode ter excepções deve estar delimitado
pela palavra chave try e parêntesis ( {} ). Depois do bloco
try deve seguir pelo menos um bloco catch que se chama o
manipulador da excepção. Este bloco também deve estar em
parêntesis ( {} ).
É permitido usar vários blocos catch após do bloco try.
7. A excepção é gerada quando a instrução throw é activada. Neste
caso as seguintes acções vão ser executadas:
7.1. Procuramos o respectivo manipulador, i.e. o primeiro bloco
catch que tem o argumento associado com a instrução throw.
7.2. Se o manipulador for encontrado começamos o processo que se
chama “stack unwinding”. Isto significa que todos os destrutores
para os objectos locais construídos a partir do ínicio do bloco catch
vão ser invocados. De notar que os destrutores vão ser chamados só
para os objectos que foram construídos completamente.
7.3. O controlo vai ser passado ao manipulador que foi encontrado.
8. A instrução throw passa o controlo ao primeiro bloco
catch, i.e. ao manipulador cujo bloco try está a ser executado.
9. A palavra chave throw pode ser usada das seguintes formas:
9.1. throw operand; - activa o indicador (operador) da excepção,
que vai ser passado ao respectivo manipulador.
9.2. throw ; - activa novamente a mesma excepção.
9.3. void f() throw(X,Y) { ... } - específica a lista (X,Y) das excepções
que podem ser geradas pela função f. Se a função f gerar outras
excepções (que não são X nem Y), a função especial que se chama
unexpected vai ser chamada automaticamente.
10. A lista das excepções não faz parte da função. Para esta lista
existem os seguintes convénios:
10.1. A função sem a lista pode gerar qualquer excepção.
10.2. A função com a lista vazia tal como throw() não pode gerar
qualquer excepção.
10.3. A função que pode gerar a excepção do tipo (da classe) X pode
também gerar qualquer excepção da classe Y (XY) se a classe
base X tiver o atributo público, por exemplo class Y : public X {...};
11. O manipulador das excepções pode ser apresentado das duas
seguintes formas:
try { .......................... }
A:
catch(Type Object) { ............ }
B:
catch(...) { ............ }
O tipo do argumento Object pode ser: Type, Type&, const Type,
const Type&. O argumento é aceite se pelo menos uma das
seguintes regras for válida:
a) o Objecto tem o tipo Type.
b) Type é um tipo da classe base para o Objecto (que pode ser
aceite).
c) Type é um ponteiro e o Objecto tem o tipo ponteiro que
pode ser convertido ao Type através de uso das regras da linguagem.
12. O manipulador catch(...) trata qualquer excepção independente
do seu tipo.
13. A procura nos blocos catch é feita pela sequência em que
estes blocos se encontram no programa.
14. Vamos assumir que temos a classe Base e a classe Derivada
(Base  Derivada). Neste caso, se a classe Base for encontrada antes
da classe Derivada vamos ter um erro porque a classe Derivada
nunca vai ser executada.
15. O manipulador catch(...) deve ser sempre o último bloco na lista
dos blocos catch.
16. Depois de terminar o bloco catch o controlo vai ser passado
ao fim da lista que contém todos os blocos catch e depois para o
bloco try.
17. Para sair dos blocos catch ou try não é permitido usar a
instrução goto.
18. A função terminate é chamada nas seguintes ocasiões:
18.1. É impossível encontrar o respectivo bloco catch.
18.2. Existem erros na pilha.
18.3. Existem erros no destrutor que destroí os objectos
automáticos (locais).
19. Por defeito a função unexpected chama a função terminate.
Podemos redefinir estas funções usando funções da biblioteca tais
como set_terminate e set_unexpected.
Download

ppt1