¿Qué es el desbobinado de la pila? ¡Busqué pero no pude encontrar una respuesta esclarecedora!
¿Qué es el desbobinado de la pila? ¡Busqué pero no pude encontrar una respuesta esclarecedora!
Respuestas:
El desenrollado de la pila generalmente se menciona en relación con el manejo de excepciones. Aquí hay un ejemplo:
void func( int x )
{
char* pleak = new char[1024]; // might be lost => memory leak
std::string s( "hello world" ); // will be properly destructed
if ( x ) throw std::runtime_error( "boom" );
delete [] pleak; // will only get here if x == 0. if x!=0, throw exception
}
int main()
{
try
{
func( 10 );
}
catch ( const std::exception& e )
{
return 1;
}
return 0;
}
Aquí la memoria asignada para pleak
se perderá si se produce una excepción, mientras que la memoria asignada a s
será liberada adecuadamente por el std::string
destructor en cualquier caso. Los objetos asignados en la pila se "desenrollan" cuando se sale del alcance (aquí el alcance es de la función func
). Esto lo hace el compilador insertando llamadas a destructores de variables automáticas (pila).
Ahora, este es un concepto muy poderoso que conduce a la técnica llamada RAII , que es Adquisición de recursos es inicialización , que nos ayuda a administrar recursos como memoria, conexiones de bases de datos, descriptores de archivos abiertos, etc. en C ++.
Ahora eso nos permite ofrecer garantías de seguridad excepcionales .
delete [] pleak;
solo se alcanza si x == 0.
Todo esto se relaciona con C ++:
Definición : a medida que crea objetos estáticamente (en la pila en lugar de asignarlos en la memoria de almacenamiento dinámico) y realiza llamadas a funciones, se "apilan".
Cuando se sale de un alcance (cualquier cosa delimitada por {
y }
) (al usar return XXX;
, llegar al final del alcance o lanzar una excepción) todo lo que está dentro de ese alcance se destruye (los destructores son llamados para todo). Este proceso de destruir objetos locales y llamar a destructores se denomina desenrollado de pila.
Tiene los siguientes problemas relacionados con el desenrollado de la pila:
evitar fugas de memoria (cualquier cosa asignada dinámicamente que no sea administrada por un objeto local y limpiada en el destructor se filtrará) - vea RAII referido por Nikolai, y la documentación para boost :: scoped_ptr o este ejemplo de uso de boost :: mutex :: scoped_lock .
coherencia del programa: las especificaciones de C ++ establecen que nunca debe lanzar una excepción antes de que se haya manejado cualquier excepción existente. Esto significa que el proceso de desenrollado de la pila nunca debería generar una excepción (ya sea usar solo código garantizado para no arrojar destructores, o rodear todo en destructores con try {
y } catch(...) {}
).
Si algún destructor arroja una excepción durante el desbobinado de la pila, terminará en la tierra del comportamiento indefinido que podría causar que su programa finalice inesperadamente (comportamiento más común) o que el universo termine (teóricamente posible pero aún no se ha observado en la práctica).
En un sentido general, una pila "desenrollar" es prácticamente sinónimo del final de una llamada a la función y la posterior aparición de la pila.
Sin embargo, específicamente en el caso de C ++, el desbobinado de la pila tiene que ver con cómo C ++ llama a los destructores para los objetos asignados desde el inicio de cualquier bloque de código. Los objetos que se crearon dentro del bloque se desasignan en orden inverso a su asignación.
try
bloques. Los objetos de pila asignados en cualquier bloque (estén try
o no) están sujetos a desenrollarse cuando el bloque sale.
El desbobinado de la pila es un concepto principalmente de C ++, que trata de cómo se destruyen los objetos asignados a la pila cuando se sale de su alcance (ya sea normalmente o mediante una excepción).
Digamos que tiene este fragmento de código:
void hw() {
string hello("Hello, ");
string world("world!\n");
cout << hello << world;
} // at this point, "world" is destroyed, followed by "hello"
No sé si leíste esto todavía, pero el artículo de Wikipedia sobre la pila de llamadas tiene una explicación decente.
Desbobinado:
Al regresar de la función llamada, el cuadro superior saldrá de la pila, quizás dejando un valor de retorno. El acto más general de extraer uno o más cuadros de la pila para reanudar la ejecución en otra parte del programa se denomina desenrollado de la pila. y debe realizarse cuando se usan estructuras de control no locales, como las que se usan para el manejo de excepciones. En este caso, el marco de la pila de una función contiene una o más entradas que especifican manejadores de excepciones. Cuando se lanza una excepción, la pila se desenrolla hasta que se encuentra un controlador que está preparado para manejar (capturar) el tipo de excepción lanzada.
Algunos lenguajes tienen otras estructuras de control que requieren un desenrollado general. Pascal permite que una instrucción goto global transfiera el control de una función anidada a una función externa previamente invocada. Esta operación requiere que la pila se desenrolle, eliminando tantos marcos de pila como sea necesario para restaurar el contexto adecuado para transferir el control a la declaración de destino dentro de la función externa adjunta. Del mismo modo, C tiene las funciones setjmp y longjmp que actúan como gotos no locales. Common Lisp permite controlar lo que sucede cuando la pila se desenrolla utilizando el operador especial de desenrollado-protección.
Al aplicar una continuación, la pila se desenrolla (lógicamente) y luego se rebobina con la pila de la continuación. Esta no es la única forma de implementar continuaciones; por ejemplo, usando múltiples pilas explícitas, la aplicación de una continuación simplemente puede activar su pila y liquidar un valor para ser pasado. El lenguaje de programación Scheme permite ejecutar thunks arbitrarios en puntos específicos al "desenrollar" o "rebobinar" de la pila de control cuando se invoca una continuación.
Inspección [editar]
Leí una publicación de blog que me ayudó a entender.
¿Qué es el desbobinado de la pila?
En cualquier lenguaje que admita funciones recursivas (es decir, casi todo excepto Fortran 77 y Brainf * ck), el tiempo de ejecución del lenguaje mantiene una pila de las funciones que se están ejecutando actualmente. El desbobinado de la pila es una forma de inspeccionar y posiblemente modificar esa pila.
¿Por qué querrías hacer eso?
La respuesta puede parecer obvia, pero hay varias situaciones relacionadas, pero sutilmente diferentes, en las que el desenrollado es útil o necesario:
- Como mecanismo de control de flujo de tiempo de ejecución (excepciones de C ++, C longjmp (), etc.).
- En un depurador, para mostrar al usuario la pila.
- En un generador de perfiles, para tomar una muestra de la pila.
- Desde el programa en sí (como desde un controlador de bloqueo para mostrar la pila).
Estos tienen requisitos sutilmente diferentes. Algunos de estos son críticos para el rendimiento, otros no. Algunos requieren la capacidad de reconstruir registros desde el marco externo, otros no. Pero entraremos en todo eso en un segundo.
Puedes encontrar la publicación completa aquí .
Todos han hablado sobre el manejo de excepciones en C ++. Pero, creo que hay otra connotación para el desenrollado de la pila y que está relacionada con la depuración. Un depurador tiene que desenrollar la pila cada vez que se supone que debe ir a un cuadro anterior al cuadro actual. Sin embargo, esto es una especie de desenrollado virtual, ya que necesita rebobinar cuando vuelve al marco actual. El ejemplo de esto podría ser comandos arriba / abajo / bt en gdb.
En mi opinión, el siguiente diagrama en este artículo explica muy bien el efecto del desbobinado de la pila en la ruta de la siguiente instrucción (que se ejecutará una vez que se arroje una excepción que no se haya detectado):
En la foto:
En el segundo caso, cuando se produce una excepción, la pila de llamadas de función se busca linealmente para el controlador de excepciones. La búsqueda finaliza en la función con un controlador de excepciones, es decir, main()
con un try-catch
bloque adjunto , pero no antes de eliminar todas las entradas anteriores de la pila de llamadas de función.
El tiempo de ejecución de C ++ destruye todas las variables automáticas creadas entre lanzar y atrapar. En este sencillo ejemplo a continuación, f1 () throws y main () catches, entre objetos de tipo B y A se crean en la pila en ese orden. Cuando f1 () arroja, se llaman los destructores de B y A.
#include <iostream>
using namespace std;
class A
{
public:
~A() { cout << "A's dtor" << endl; }
};
class B
{
public:
~B() { cout << "B's dtor" << endl; }
};
void f1()
{
B b;
throw (100);
}
void f()
{
A a;
f1();
}
int main()
{
try
{
f();
}
catch (int num)
{
cout << "Caught exception: " << num << endl;
}
return 0;
}
La salida de este programa será
B's dtor
A's dtor
Esto se debe a que la pila de llamadas del programa cuando se lanza f1 () parece
f1()
f()
main()
Entonces, cuando se activa f1 (), la variable automática b se destruye, y luego cuando se activa f (), la variable automática a se destruye.
Espero que esto ayude, ¡feliz codificación!
Cuando se produce una excepción y el control pasa de un bloque try a un controlador, el tiempo de ejecución de C ++ llama a los destructores para todos los objetos automáticos construidos desde el comienzo del bloque try. Este proceso se llama desbobinado de pila. Los objetos automáticos se destruyen en orden inverso a su construcción. (Los objetos automáticos son objetos locales que se declararon automáticos o se registraron, o no se declararon estáticos o externos. Un objeto automático x se elimina cada vez que el programa sale del bloque en el que se declara x).
Si se produce una excepción durante la construcción de un objeto que consiste en subobjetos o elementos de matriz, los destructores solo se invocan para aquellos subobjetos o elementos de matriz construidos con éxito antes de que se lance la excepción. Solo se llamará a un destructor para un objeto estático local si el objeto se construyó correctamente.
En la pila Java, el desenrollado o desenrollado no es muy importante (con el recolector de basura). En muchos documentos de manejo de excepciones, vi este concepto (desbobinado de pila), en especial, esos escritores se ocupan del manejo de excepciones en C o C ++. con try catch
bloques que no debemos olvidar: pila libre de todos los objetos después de bloques locales .