¿Por qué mi excepción en destructor no activa std::terminate?

3 minutos de lectura

avatar de usuario de amit singh
amit singh

Soy muy consciente del hecho de que no se debe lanzar ninguna excepción en destructor.

Pero como parte de controlar este concepto, codifiqué este ejemplo: –

#include <iostream>

class A {
private: 
    int i;

public:
    A()  { i = 10;   }
    ~A() { throw 30; }
};
int main(){
    try{
        A();
        throw 10;
    }
    catch (int i) {
        std::cout << i << std::endl;
        std::cout << "exception caught" << std::endl;
    }
}

Según tengo entendido, este programa debe finalizar llamando a std::terminate() ya que habrá dos excepciones al mismo tiempo. Pero, este programa está dando el siguiente resultado: –

30
exception caught

¿Puede alguien explicarme la lógica detrás de esto en cuanto a por qué esto no está terminando?

  • Por qué throw 10; se espera que se ejecute?

    – dmi

    23 de marzo de 2017 a las 12:47

  • No se puede reproducir G++ 5.1.0: “terminar llamado después de lanzar una instancia de ‘int'”

    – Richard Critten

    23 de marzo de 2017 a las 12:48

  • Es bueno saber que no debes lanzar una excepción en destructor, pero ¿sabes por qué?

    – Ankur

    23 de marzo de 2017 a las 12:49

  • no se puede reproducir con clang

    – Gualterio

    23 de marzo de 2017 a las 12:50

  • No puede reproducirse ideone.com/7T91cF – terminar llamado después de lanzar una instancia de ‘int’

    – Eslava

    23 de marzo de 2017 a las 12:53

Avatar de usuario de Vittorio Romeo
Vittorio Romeo

std::terminate se llamará si se lanza una excepción durante apilar desenrollando. Eso significa que si se llama una excepción mientras se maneja otra excepcióndespués std::terminate sera llamado.

En tu ejemplo, eso no sucede – A(); va a construir y inmediatamente destruir una instancia de A. los throw 30 entonces será capturado correctamente.

Cambiando su código a:

int main(){
    try{
        A a;      // begin `a` lifetime 
        throw 10; // | throw #0           
                  // | end `a` lifetime   
                  // throw #1
    }
    catch(int i){
        cout<<i<<endl;
        cout<<"exception caught"<<endl;
    }
}

garantizará que std::terminate sera llamado. En este caso, a será destruido y arrojará mientras se maneja otra excepción.

ejemplo de coliru vivo


Información Adicional:


Tenga en cuenta que en C++ 11 y superiorsu fragmento de código llamará std::terminate y proporcionarle una advertencia:

main.cpp: En el destructor ‘A::~A()’:

main.cpp:16:15: advertencia: throw siempre llamará a terminar()
[-Wterminate]

     throw 30;

           ^~

principal.cpp:16:15: nota: en C ++ 11, los destructores predeterminados son noexcept

Terminar llamado después de lanzar una instancia de ‘int’

bash: línea 7: 1505 Anulado (núcleo volcado) ./a.out

Como se ve en la salida del compilador, desde C++11 los destructores son implícitamente noexcept(true). Si desea evitar este comportamiento, simplemente puede marcarlos como noexcept(false). Ejemplo:

~A() noexcept(false)
{
    throw 30;
}

ejemplo en vivo en coliru

  • Realmente std::terminate llamado con variable sin nombre también. De acuerdo a iostream.h OP usa compilador antiguo

    – Eslava

    23 de marzo de 2017 a las 12:52

  • @Slava: ese es un problema no relacionado. En C ++ 11, los destructores están implícitamente noexcept. Modificaré mi respuesta.

    – Vittorio Romeo

    23 de marzo de 2017 a las 12:54

En tu ejemplo, A() construir una variable temporal para A luego lo destruye inmediatamente. De este modo throw 10; nunca se ejecuta.

los throw declaración que tiene lugar está en el destructor para A. Al ejecutar A::~A(), el programa no se está desenrollando (es decir, limpiando el estado de una excepción) en ese momento. Ver “Destructores que tiran” por ejemplo.

¿Ha sido útil esta solución?