Controlo de Excepções
void f(X&){}
void ff(void) { X x;
cout << "f (call)" << endl;
f(x);
cout << "f (return)" << endl;
}
class X {
public:
X(const X&) { cout << "copy constructor\n"; } X() { cout << "constructor\n"; }
virtual ~X() { cout << ”destructor\n"; } };
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;
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
throw &x;
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 copy constructor destructor
catch
destructor destructor catch(X x)
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 destructor catch
throw &x;
catch(X* x) static X x;
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 catch(X& x)
static X x;
slicing problem
class X{ ... };
void f()
{ static X x;
...
throw &x; //ponteiro }
void ff()
{ try { f(); }
catch(X *x) { ... } // ponteiro }
// global
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();
}
void fff() {
try { ff(); }
catch (X x) { cerr << x.f(); }
} // chama X::f(), nunca Z::f()
slicing problem
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( . . . ) throw i;
} }
catch(float f) {
. . . . }
}
extern void f();
void ff() throw (int);
void ff() throw();
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.
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.
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 (XY) 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.