Excepciones y desenredo de pila en C++
En el mecanismo de excepciones de C++, el control se mueve desde la instrucción throw hasta la primera instrucción catch que puede controlar el tipo producido. Cuando se alcanza la instrucción catch, todas las variables automáticas que están en el ámbito entre las instrucciones throw y catch se destruyen en un proceso que se conoce como desenrollado de la pila. En el desenredo de la pila, la ejecución se desarrolla del modo siguiente:
El control alcanza la instrucción
try
mediante la ejecución de secuencial normal. La sección protegida en el bloquetry
se ejecuta.Si no se produce ninguna excepción durante la ejecución de la sección protegida, las cláusulas
catch
que siguen al bloquetry
no se ejecutan. La ejecución continúa en la instrucción después de la última cláusulacatch
que sigue al bloquetry
asociado.Si se produce una excepción durante la ejecución de la sección protegida o en cualquier rutina a la que la sección protegida llama directa o indirectamente, se crea un objeto de excepción desde el objeto creado por el operando
throw
. (Esto implica que puede haber un constructor de copia) En este punto, el compilador busca una cláusulacatch
en un contexto de ejecución superior que pueda controlar una excepción del tipo que se produce, o un controlador decatch
que pueda controlar cualquier tipo de excepción. Los controladores decatch
se examinan en orden de aparición después del bloquetry
. Si no se encuentra ningún controlador adecuado, se examina el siguiente bloquetry
de inclusión dinámica. Este proceso continúa hasta que se examina el bloquetry
de inclusión extremo.Si no se ha encontrado todavía un controlador coincidente, o si se produce una excepción durante el proceso de desenredo pero antes de que el controlador obtenga el control, se llama a la función
terminate
predefinida en tiempo de ejecución. Si se produce una excepción después de que se produzca la excepción pero antes de que empiece el desenredo, se llama aterminate
.Si se encuentra un controlador de
catch
coincidente y detecta por valor, su 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, comienza el proceso de desenredo de la pila. Esto implica la destrucción de todos los objetos automáticos que estaban construidos totalmente, pero todavía no destruidos, entre el principio del bloquetry
asociado al controlador decatch
y el sitio de producción de la excepción. La destrucción se produce en orden inverso al de construcción. Se ejecuta el controlador decatch
y el programa reanuda la ejecución después del último controlador, es decir, en la primera instrucción o construcción que no es un controlador decatch
. El control solo puede entrar en un controlador decatch
a través de una excepción producida, nunca a través de una instruccióngoto
o una etiquetacase
en una instrucciónswitch
.
Ejemplo de desenrollado de pila
En 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 de C
a la instrucción catch de main
y desenreda cada función a lo largo del recorrido. Observe el orden en que se crean los objetos Dummy
y se destruyen después cuando salen del ámbito. Observe también que ninguna función se completa excepto main
, que contiene la instrucción catch. La función A
nunca vuelve de la llamada a B()
y B
nunca vuelve de la llamada a C()
. Si quita los comentarios de la definición del puntero de Dummy
y la correspondiente instrucción delete, y ejecuta después el programa, observe que el puntero nunca se elimina. Esto muestra lo que puede suceder cuando las funciones no proporcionan una garantía de excepción. Para obtener más información, vea How to: Design for Exceptions. Si marca como comentario la instrucción catch, puede observar lo que sucede 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.
*/
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente GitHub Issues como mecanismo de comentarios sobre el contenido y lo sustituiremos por un nuevo sistema de comentarios. Para más información, vea:Enviar y ver comentarios de