2010-12-22 12 views
23

Me acaba de ocurrir. Me pregunté cómo se liberan los recursos en el siguiente caso.¿Qué sucede con el destructor de la clase base si un destructor de clase derivado lanza una excepción?

class Base { 
    Resource *r; 

public: 
    Base() { /* ... */ } 
    ~Base() { 
    delete r; 
    } 
}; 

class Derived : public Base { 
public: 
    Derived() { /* ... */ } 
    ~Derived() { 
    /* Suddenly something here throws! */ 
    } 
}; 

int main() { 
    try { 
    Derived d; 
    } catch(...) { 
    /* what happened with Base::r !? */ 
    } 
} 

¿Se invocará el destructor de la clase base si arroja el destructor de clase derivado? ¿O habrá una fuga?

+6

De memoria, las definiciones de la norma de lo que sucederá si un destructor lanza son tan increíblemente compleja y generalmente horrible que nadie nunca, nunca lanza desde un destructor. También estoy bastante seguro de que todos los contenedores estándar suponen que los destructores no son inteligentes. Básicamente, aunque creo que técnicamente, es legal y definido, la realidad es que nadie lo hace y por muy buenas razones, así que no lo hagas nunca. – Puppy

+1

La muestra que tiene allí no debería ser un problema. d está saliendo de su alcance simplemente en la forma normal de las cosas. Donde las cosas pueden ponerse feas es si la razón d se está destruyendo es que la pila se desenrolla como parte de manejar otra excepción. Es esa posibilidad la que conduce a reglas generales sobre nunca lanzar un destructor. –

Respuesta

20

Según §15.2/2:

Un objeto que está construido parcialmente o parcialmente destruidas tendrá destructores ejecutados para todos sus subobjetos totalmente construido, es decir, para subobjetos para los que el constructor ha terminado la ejecución y el destructor aún no ha comenzado la ejecución.

Por lo que debe llamarse al destructor de la clase base. Es decir, al igual que sabemos que esto va a limpiar la clase base:

#include <iostream> 

struct foo 
{ 
    ~foo() 
    { 
     std::cout << "clean" << std::endl; 
    } 
}; 

struct bar : foo 
{ 
    bar() 
    { // foo is initialized... 
     throw 0; // ...so its destructor is run 
    } 
}; 

int main() 
{ 
    try 
    { 
     bar b; 
    } 
    catch (...) 
    { 
     std::cerr << "caught" << std::endl; 
    } 
} 

Y que esto va a limpiar el miembro:

#include <iostream> 

struct foo 
{ 
    ~foo() 
    { 
     std::cout << "clean" << std::endl; 
    } 
}; 

struct bar 
{ 
    ~bar() 
    { // f has been initialized... 
     throw 0; // ...so its destructor will run 
    } 

    foo f; 
}; 

int main() 
{ 
    try 
    { 
     bar b; 
    } 
    catch (...) 
    { 
     std::cerr << "caught" << std::endl; 
    } 
} 

Esto también va a limpiar la clase base:

#include <iostream> 

struct foo 
{ 
    ~foo() 
    { 
     std::cout << "clean" << std::endl; 
    } 
}; 

struct bar : foo 
{ 
    ~bar() 
    { // foo has been initialized... 
     throw 0; // ...so its destructor will run 
    } 
}; 

int main() 
{ 
    try 
    { 
     bar b; 
    } 
    catch (...) 
    { 
     std::cerr << "caught" << std::endl; 
    } 
} 

Esa es mi comprensión de la cita.

+0

En VC++ 9 base destructor se llama. – sharptooth

+0

@sharp: Y en MSVC10. @Kirill: Gracias, estoy oxidado. :) – GManNickG

+1

Gracias por la respuesta. ¡Creo que es acertado! –

1

Se llamará al destructor base.

En Efectivo C++, Meyers recomienda que las excepciones no dejen destructores. Capture la excepción dentro del destructor y maneje, trague o termine.

+0

¿Tiene una justificación para su reclamo? – GManNickG

+1

Escribes "No creo". Y yo sí pienso ¿Y qué? OP tiene un puntaje de reputación muy alto y obviamente quiere obtener una respuesta más clara. –

+0

Escribí "No creo" porque eso es lo que entendí. Ahora lo he comprobado, y sí, los destructores de base son llamados. Todavía hay un problema sobre cuánto del destructor de clase derivado se ha ejecutado antes de la excepción. – DanS

1

De hecho, se llama al destructor de la clase base. Código de ejemplo:

#include 
#include 

class Base { 
public: 
    Base() { /* ... */ } 
    ~Base() { 
    printf("Base\n"); 
    } 
}; 

class Derived : public Base { 
public: 
    Derived() { /* ... */ } 
    ~Derived() { 
    printf("Derived\n"); 
    throw 1; 
    } 
}; 

int main() { 
    try { 
    Derived d; 
    } catch(...) { 
    printf("CAUGHT!\n"); 
    } 
} 

Esta impresora:

Derived 
Base 
CAUGHT! 
+1

Bueno. Pero GMan respondió antes. –

+1

@Alexey - no es una carrera, es la calidad de la respuesta lo que cuenta. –

+0

@Brian: para no tocar mi propio cuerno, pero como mi respuesta ya existía, no hay nada más que agregar para responder directamente la pregunta. Podría decirse que esta respuesta es peor porque es solo una prueba de que algún compilador bajo esta condición específica produjo un resultado específico, no una prueba de que esté garantizado. – GManNickG

Cuestiones relacionadas