Exceções e pilha desenrolar em C++
O mecanismo de exceção C++, controle vai da instrução throw a primeira instrução catch que pode manipular o tipo lançada.Quando a instrução catch é alcançada, todas as variáveis automática que estão no escopo entre o throw e instruções catch serão destruídos em um processo que é conhecido como o desenrolamento de pilha.Em a pilha que desenrola, a execução continua a seguinte maneira:
O controle atingir a declaração de try pela execução seqüencial normal.A seção guardada no bloco de try é executada.
Se nenhuma exceção é lançada durante a execução da seção guardada, as cláusulas de catch após o bloco de try não são executadas.Excução continua na instrução após a última cláusula de catch após o bloco associado de try .
Se uma exceção é lançada durante a execução da seção guardada ou rotina que a seção guardada chamar direta ou indiretamente, um objeto de exceção é criado a partir do objeto que é criado pelo operando de throw .(Isso significa que um construtor de impressão pode ser criação.) Em este ponto, o compilador procura uma cláusula de catch em um contexto mais alto de execução que pode manipular uma exceção do tipo que é acionada, ou para um manipulador de catch que pode manipular qualquer tipo de exceção.Os manipuladores de catch são examinados por ordem de sua aparência após o bloco de try .Se nenhum manipulador apropriado é encontrado, o bloco dinamicamente incluindo a seguir de try é examinado.Esse processo continua até que o bloco delimitador mais externo de try foi revisado.
Se um manipulador compatível não é encontrado ainda, ou se ocorrer uma exceção durante o processo de desenrolamento mas antes que o tratador obtém o controle, a função predefinida terminate de tempo de execução é chamada.Se uma exceção ocorre após a exceção é lançada mas antes do desenrolamento começar, terminate é chamado.
Se um manipulador de catch de correspondência for encontrada, e captura por valor, o parâmetro tipo é inicializado copiando o objeto de exceção.Se captura por referência, o parâmetro é inicializado para fazer referência ao objeto de exceção.Depois que o parâmetro tipo é inicializado, o processo de desenrolar a pilha começa.Isso envolve a destruição de todos os objetos que automático era totalmente construir- mas ainda não destructed- entre o início do bloco de try que está associado com o manipulador de catch e o site throw de exceção.A destruição ocorre na ordem inversa de compilação.O manipulador de catch é executado e a execução do programa resumos após o último manipulador- que é, na primeira instrução ou compilação que não é um manipulador de catch .O controle só pode inserir um manipulador de catch com uma exceção acionada, nunca através de uma instrução de goto ou de um rótulo de case em uma instrução de switch .
Exemplo de desenrolamento de pilha
O exemplo a seguir demonstra como a pilha é desenrolada quando uma exceção é lançada.A thread em execução vai da instrução throw C na instrução catch em main, e desenrola cada função ao longo do caminho.Observe a ordem em que os objetos de Dummy são criados e destruídos em como sai do escopo.Também observe que nenhuma função concluir main, exceto que contém a instrução catch.A função A nunca retornará de sua chamada a B(), e B nunca retornará de sua chamada a C().Se você tire comentários da definição do ponteiro de Dummy e a declaração correspondente de exclusão, e executado no programa, observa que o ponteiro é excluído nunca.Isso mostra o que pode acontecer quando as funções não fornecem uma garantia de exceção.Para obter mais informações, consulte como: design para exceções.Se você comente a instrução catch, você poderá notar o que acontece quando um programa finaliza por causa de uma exceção sem tratamento.
#include <string>
#include <iostream>
using namespace std;
class MyException{};
class Dummy
{
public:
Dummy(string s) : MyName(s) { PrintMsg("Created Dummy:"); }
Dummy(const Dummy& other) : MyName(other.MyName){ PrintMsg("Copy created Dummy:"); }
~Dummy(){ PrintMsg("Destroyed Dummy:"); }
void PrintMsg(string s) { cout << s << MyName << endl; }
string MyName;
int level;
};
void C(Dummy d, int i)
{
cout << "Entering FunctionC" << endl;
d.MyName = " C";
throw MyException();
cout << "Exiting FunctionC" << endl;
}
void B(Dummy d, int i)
{
cout << "Entering FunctionB" << endl;
d.MyName = "B";
C(d, i + 1);
cout << "Exiting FunctionB" << endl;
}
void A(Dummy d, int i)
{
cout << "Entering FunctionA" << endl;
d.MyName = " A" ;
// Dummy* pd = new Dummy("new Dummy"); //Not exception safe!!!
B(d, i + 1);
// delete pd;
cout << "Exiting FunctionA" << endl;
}
int main()
{
cout << "Entering main" << endl;
try
{
Dummy d(" M");
A(d,1);
}
catch (MyException& e)
{
cout << "Caught an exception of type: " << typeid(e).name() << endl;
}
cout << "Exiting main." << endl;
char c;
cin >> c;
}
/* Output:
Entering main
Created Dummy: M
Copy created Dummy: M
Entering FunctionA
Copy created Dummy: A
Entering FunctionB
Copy created Dummy: B
Entering FunctionC
Destroyed Dummy: C
Destroyed Dummy: B
Destroyed Dummy: A
Destroyed Dummy: M
Caught an exception of type: class MyException
Exiting main.
*/