top priority empty c++ stack
boost::mutex::scoped_lock

priority - stack top c++



¿Qué es el desenrollamiento de la pila? (11)

Cuando se lanza una excepción y el control pasa de un bloque try a un manejador, el tiempo de ejecución C ++ llama a los destructores para todos los objetos automáticos construidos desde el comienzo del bloque try. Este proceso se llama desenrollamiento de la pila. Los objetos automáticos se destruyen en orden inverso a su construcción. (Los objetos automáticos son objetos locales que han sido declarados automáticos o registrados, o no declarados 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 lanza una excepción durante la construcción de un objeto que consiste en subobjetos o elementos de matriz, los destructores solo se llaman para aquellos subobjetos o elementos de matriz construidos con éxito antes de que se lanzara la excepción. Un destructor para un objeto estático local solo se invocará si el objeto se construyó correctamente.

¿Qué es el desenrollamiento de la pila? Buscó pero no pudo encontrar una respuesta esclarecedora.


El desenrollado de la pila es principalmente un concepto de C ++, que trata sobre cómo se destruyen los objetos asignados por la pila cuando se abandona su alcance (normalmente o a través de una excepción).

Digamos que tienes 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"


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 lanza una excepción, mientras que la memoria asignada a s será liberada adecuadamente por 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 que inserta llamadas a destructores de variables automáticas (de pila).

Ahora bien, este es un concepto muy poderoso que lleva a la técnica llamada RAII , es la Inicialización de Adquisición de Recursos , que nos ayuda a administrar recursos como la memoria, conexiones de bases de datos, descriptores de archivos abiertos, etc. en C ++.

Ahora eso nos permite proporcionar garantías de seguridad excepcionales .


El tiempo de ejecución de C ++ destruye todas las variables automáticas creadas entre throw & catch. En este ejemplo simple debajo de f1 () arroja y captura principal (), entre los objetos de tipo B y A se crean en la pila en ese orden. Cuando f1 () arroja, se invocan los destructores 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; }

El resultado de este programa será

B''s dtor A''s dtor

Esto se debe a que la pila de llamadas del programa cuando arroja f1 () se ve como

f1() f() main()

Por lo tanto, cuando se quita f1 (), la variable automática b se destruye, y luego cuando se quita f () la variable automática a se destruye.

Espero que esto ayude, ¡feliz codificación!


En Java, la pila que se deshace o deshace no es muy importante (con el recolector de basura). En muchos documentos de manejo de excepciones, vi este concepto (desenrollado de la pila), en especial, aquellos writers tratan con el manejo de excepciones en C o C ++. con try catch blocks que no debemos olvidar: pila gratis de todos los objetos después de bloques locales .


En un sentido general, una pila "desenrollar" es casi sinónimo del final de una llamada de función y el estallido posterior de la pila.

Sin embargo, específicamente en el caso de C ++, el desenrollado de la pila tiene que ver con cómo C ++ llama a los destructores para los objetos asignados desde el comienzo de cualquier bloque de código. Los objetos que se crearon dentro del bloque se desasignan en el orden inverso de su asignación.


IMO, el siguiente diagrama en este article explica maravillosamente el efecto del desenrollado de la pila en la ruta de la siguiente instrucción (que se ejecutará una vez que se lanza una excepción que no se ha capturado):

En la foto:

  • La primera es una ejecución de llamada normal (sin excepción).
  • Abajo, cuando se lanza una excepción.

En el segundo caso, cuando se produce una excepción, la pila de llamadas de función se busca linealmente para el manejador de excepciones. La búsqueda finaliza en la función con controlador de excepciones, es decir, main() con el bloque try-catch adjunto, pero no antes de eliminar todas las entradas anteriores de la pila de llamadas a funciones.


Leí una publicación de blog que me ayudó a entender.

¿Qué es el desenrollamiento de la pila?

En cualquier idioma 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 desenrollado 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, aunque sutilmente diferentes, en las que el desenrollamiento es útil o necesario:

  1. Como un mecanismo de flujo de control de tiempo de ejecución (excepciones de C ++, C longjmp (), etc.).
  2. En un depurador, para mostrar al usuario la pila.
  3. En un generador de perfiles, para tomar una muestra de la pila.
  4. 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 here .


No sé si aún lees esto, pero el artículo de Wikipedia sobre la pila de llamadas tiene una explicación decente.

Desbobinar:

Al regresar de la función llamada se abrirá el marco superior de la pila, quizás dejando un valor de retorno. El acto más general de extraer uno o más marcos de la pila para reanudar la ejecución en otro lugar del programa se llama desenrollado de pila y se debe realizar cuando se usan estructuras de control no locales, como las utilizadas para el manejo de excepciones. En este caso, el marco de 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 (atrapar) el tipo de la excepción lanzada.

Algunos idiomas tienen otras estructuras de control que requieren un desenrollado general. Pascal permite que una declaración goto global transfiera el control desde una función anidada a una función externa invocada anteriormente. 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 instrucció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 el control de lo que sucede cuando la pila se desenrolla utilizando el operador especial de desenrollar y proteger.

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, al usar pilas múltiples y explícitas, la aplicación de una continuación simplemente puede activar su pila e impulsar un valor para pasar. El lenguaje de programación Scheme permite que se ejecuten thunk arbitrarios en puntos específicos al "desenrollar" o "rebobinar" la pila de control cuando se invoca una continuación.

Inspección [editar]


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 del montón) y realiza llamadas a funciones, se "apilan".

Cuando se sale de un ámbito (cualquier cosa delimitada por { y } ) (utilizando return XXX; llegando al final del alcance o lanzando una excepción) se destruye todo dentro de ese alcance (se invocan destructores para todo). Este proceso de destruir objetos locales y llamar a destructores se llama desenrollado de pila.

Tiene los siguientes problemas relacionados con el desenrollado de la pila:

  1. evitando pérdidas de memoria (todo lo que se haya asignado dinámicamente que no esté gestionado por un objeto local y se haya limpiado 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 .

  2. Consistencia del programa: las especificaciones de C ++ establecen que nunca debe lanzar una excepción antes de que se haya manejado una excepción existente. Esto significa que el proceso de desenrollado de la pila nunca debe arrojar una excepción (ya sea que use solo código garantizado para no arrojar destructores, o rodee todo en los destructores con try { y } catch(...) {} ).

Si cualquier destructor arroja una excepción durante el desenrollamiento de la pila, terminará en un terreno de comportamiento indefinido que podría hacer que su programa trepe inesperadamente (el comportamiento más común) o que el universo termine (teóricamente posible pero aún no se ha observado en la práctica).


Todos han hablado sobre el manejo de excepciones en C ++. Pero, creo que hay otra connotación para desenrollar la pila y que está relacionada con la depuración. Un depurador tiene que deshacerse de la pila cada vez que se supone que va a un marco anterior al marco actual. Sin embargo, esto es una especie de desenrollado virtual, ya que necesita rebobinarse cuando vuelve al fotograma actual. El ejemplo para esto podría ser comandos up / down / bt en gdb.