Pregunta

En el siguiente código, la variable basada en pila 'ex' se genera y se captura en una función más allá del alcance en el que se declaró ex.Esto me parece un poco extraño, ya que (AFAIK) las variables basadas en pila no se pueden usar fuera del alcance en el que fueron declaradas (la pila está desenrollada).

void f() {
    SomeKindOfException ex(...);
    throw ex;
}

void g() {
    try {
        f();
    } catch (SomeKindOfException& ex) {
        //Handling code...
    }
}

Agregué una declaración de impresión al destructor de SomeKindOfException y muestra que ex se destruye una vez que sale del alcance en f() pero luego queda atrapado en g() y se destruye nuevamente una vez que sale del alcance allí también.

¿Alguna ayuda?

¿Fue útil?

Solución

El objeto de excepción se copia a un lugar especial para sobrevivir a la pila desenrollar. La razón se ven dos destrucciones se debe a que al salir de f () la excepción original se destruye y al salir de g () la copia se destruye.

Otros consejos

El objeto se copia en un objeto de excepción que sobrevive Stack-desenrollar. Donde el recuerdo de ese objeto viene de está especificado. Para objeto grande, es probable que se malloc'ed, y para objetos más pequeños, la aplicación podría tener un búfer preasignado (i podía imaginar que esto podría ser utilizado para una excepción bad_alloc).

El ex referencia se une entonces a ese objeto excepción , que es un temporal (no tiene nombre).

C ++ estándar 15.1 / 4:

  

La memoria para la copia temporal de la excepción que es lanzado se asigna de una manera no especificada,   excepto como se indica en 3.7.3.1. El temporal persiste mientras hay un manejador de ser ejecutado por ese   excepción. En particular, si un controlador sale mediante la ejecución de un tiro; declaración, que se pasa el control a otro   controlador para la misma excepción, por lo que los restos temporales. Cuando el último controlador está ejecutando para el   salidas de excepción por cualquier medio distinto de lanzar; el objeto temporal se destruye y la puesta en práctica   puede desasignar la memoria para el objeto temporal; cualquier cancelación de asignación se realiza de una manera no especificada.   La destrucción se produce inmediatamente después de la destrucción del objeto declarado en la declaración de excepciones   en el controlador.

No hay nada más que decir.

Cuando lanzas ex que se copie en una posición de memoria especial que se utiliza para los objetos excepción lanzada. Dicha copia se lleva a cabo por el constructor de copia normal.

Esto se puede ver fácilmente desde este ejemplo:

#include <iostream>

void ThrowIt();

class TestException
{
  public:
    TestException()
    {
        std::cerr<<this<<" - inside default constructor"<<std::endl;
    }

    TestException(const TestException & Right)
    {
        (void)Right;
        std::cerr<<this<<" - inside copy constructor"<<std::endl;
    }

    ~TestException()
    {
        std::cerr<<this<<" - inside destructor"<<std::endl;    
    }
};

int main()
{
    try
    {
        ThrowIt();
    }
    catch(TestException & ex)
    {
        std::cout<<"Caught exception ("<<&ex<<")"<<std::endl;
    }
    return 0;
}

void ThrowIt()
{
    TestException ex;
    throw ex;
}

Resultado de muestra:

matteo@teolapubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic ExceptionStack.cpp -o ExceptionStack.x
matteo@teolapubuntu:~/cpp/test$ ./ExceptionStack.x 
0xbf8e202f - inside default constructor
0x9ec0068 - inside copy constructor
0xbf8e202f - inside destructor
Caught exception (0x9ec0068)
0x9ec0068 - inside destructor

Por cierto, se puede ver aquí que la posición de memoria utilizada para el objeto lanzado (0x09ec0068) es, sin duda muy lejos de la del objeto original (0xbf8e202f): la pila, como de costumbre, tiene direcciones de alto, mientras que el memoria utilizada por el objeto lanzado es bastante abajo en el espacio de direcciones virtuales. Aún así, este es un detalle de implementación, ya que, como otras respuestas señalaron, la norma no dicen nada acerca de dónde la memoria de objeto lanzado debe ser y cómo debe ser asignado.

Además de lo que la norma dice en 15.1 / 4 ( "El manejo de excepciones / Lanzar una excepción") - Cuando la memoria de la copia temporal de la excepción de ser lanzado se asigna de una manera no especificada - un par de otras trivialidades acerca de cómo se asigna el objeto de excepción son:

  • 3.7.3.1/4 ( "funciones de asignación") de la norma indica que el objeto de excepción no puede ser asignado por una expresión new o una llamada a una 'función de asignación global' (es decir., Un operator new() reemplazo). Tenga en cuenta que malloc() no es una 'función de asignación global' como se define en la norma, por lo malloc() es definitivamente una opción para asignar el objeto de excepción.

  • "Cuando se produce una excepción, se crea el objeto de excepción y un coloca generalmente en una especie de pila de datos de excepción" (Stanley Lippman, "Dentro del objeto de C ++ Modelo" - 7.2 El manejo de excepciones)

  • Desde BS "The C ++ Programming Language, 3rd Edition": ". Se requiere un C aplicación ++ tener suficiente memoria libre para poder lanzar bad_alloc en caso de agotamiento de memoria Sin embargo, es posible que tirar alguna otra excepción causará agotamiento de la memoria "(14.4.5 de agotamiento de recursos).; y "La implementación puede aplicar una amplia variedad de estrategias para el almacenamiento y transmisión de excepciones. Se garantiza, sin embargo, que no hay memoria suficiente para permitir new para lanzar el estándar fuera de la memoria excepción, bad_alloc" (14.3 excepciones Catching) .

Tenga en cuenta que las citas de BS son pre-estándar. Me parece interesante que la norma no parece tener la garantía de que BS pensó lo suficientemente importante mencionar dos veces.

Debido a que la especificación establece de manera explícita, que un objeto temporal se crea en el lugar del operando throw.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top