Excepciones y pila Unwinding en C++

En el mecanismo de excepciones de C++, los movimientos de control de la instrucción throw a la primera instrucción catch que puede controlar el tipo se produce.Cuando se alcanza la instrucción catch, todas las variables automáticas que están en el ámbito entre la captura y las instrucciones catch se destruyen en un proceso que se conoce como desenredo de la pila.En la pila que desenredo, la ejecución continúa como sigue:

  1. El Control alcanza la instrucción try por la ejecución secuencial normal.La sección guardada en el bloque try se ejecuta.

  2. Si no se produce ninguna excepción durante la ejecución de la sección guardada, las cláusulas catch que siguen el bloque try no se ejecutan.Continúa la ejecución de la instrucción después de la cláusula última catch que sigue al bloque asociado try .

  3. Si se produce una excepción durante la ejecución de la sección guardada o en cualquier rutina que la sección guardada llame a directa o indirectamente, un objeto de excepción se crea el objeto creado por el operando throw .(Esto implica que un constructor de copias puede estar implicado.) En este punto, el compilador busca una cláusula catch en un contexto más alto de la ejecución que pueda controlar una excepción del tipo que se produce, o para un controlador catch que puede administrar cualquier tipo de excepción.Examine los controladores catch en orden de aparición detrás del bloque try .Si no se encuentra ningún controlador adecuado, se examina el siguiente bloque try dinámicamente envolvente.Este proceso continúa hasta que se examine el bloque fuera try envolvente.

  4. Si aún no se ha encontrado un controlador coincidente, o si se produce una excepción durante el proceso de " desenredo pero antes de que el controlador obtiene el control, se llama a la función predefinida terminate en tiempo de ejecución.Si se produce una excepción después de que se produzca la excepción pero antes de que inicia el desenredo, se llama a terminate .

  5. Si encuentra un controlador coincidente catch , y detectada por valor, el parámetro formal se inicializa copiando el objeto de excepción.Si detecta por referencia, el parámetro se inicializa para hacer referencia al objeto de excepción.Una vez inicializado el parámetro formal, el proceso de descarga pila comienza.Esto implica la destrucción de todos los objetos automáticos que estaban totalmente construir- pero todavía no destructed- entre el principio try incrustado que se asocia el controlador catch y el sitio de captura de la excepción.Destrucción aparece en orden inverso de la construcción.Se ejecuta el controlador catch y el programa reanuda la ejecución después del último controlador- que es, en la primera instrucción o construcción que no es un controlador catch .El Control puede encontrarse sólo un controlador catch con una excepción, nunca en una instrucción goto o una etiqueta case en una instrucción switch .

Ejemplo el desenredo de la pila

El ejemplo siguiente se muestra cómo se desenreda la pila cuando se produce una excepción.La ejecución del subproceso salta de la instrucción throw en C a la instrucción catch en main, y desenredo cada función a lo largo de la forma.Observe el orden en que se crean y se destruyen los objetos Dummy mientras que salen del ámbito.Observe también que ninguna función completa excepto main, que contiene la instrucción catch.La función A nunca regresa desde la llamada a B(), y B nunca regresa desde la llamada a C().Si los comentarios de la definición del puntero Dummy y de la instrucción correspondiente de cancelación, y ejecutar el programa, observe que el puntero nunca se eliminan.Esto muestra lo que puede suceder cuando las funciones no proporcionan una garantía de la excepción.Para obtener más información, vea Cómo: Diseñar para Excepciones.Si se marque como comentario la instrucción catch, puede observar qué ocurre cuando un programa finaliza debido a una excepción no controlada.

#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.
 
*/