2012-03-10 11 views
8

He leído varios artículos aquí y en los que está bien lanzar una excepción desde el constructor. Sin embargo, he notado que no llama a destructor de la clase base o sus miembros de datos si se lanza una excepción desde el constructor. Consideremos el siguiente ejemplo:lanzando una excepción desde el constructor en C++

#include <iostream> 
using namespace std; 
struct C 
{ 
    C() { cout << __FUNCTION__ << endl; } 
    ~C() { cout << __FUNCTION__ << endl; } 
}; 

struct E: public C 
{ 
    C c; 
    E() { cout << __FUNCTION__ << endl; throw 4; } 
    ~E() { cout << __FUNCTION__ << endl; } 
}; 

int main() 
{ 
    E e; 
} 


$ g++ test.cpp; ./a.exe 
C 
C 
E 
terminate called after throwing an instance of 'int' 
Aborted (core dumped) 

En este caso, el constructor de E lanza una excepción, pero destructor de C como un miembro de datos o como una clase base no se llama. Ahora bien, si el destructor de C realiza alguna operación de limpieza, como cerrar archivos/sockets y eliminar asignaciones de heap, esto puede causar problemas.

Así que mi pregunta es por qué y cuándo está bien lanzar excepciones de los constructores.

+0

Tenga en cuenta que si detecta la excepción en 'main', se invocan los destructores. Ver [aquí] (http://ideone.com/nQemT). –

+0

Antes de acostumbrarse al uso de excepciones, le recomendamos leer [this] (http://stackoverflow.com/questions/1744070/why-should-exceptions-be-used-conservatively) y su pregunta relacionada. – Shahbaz

Respuesta

12

Si detecta el error, se ejecutará el destructor. Cuando se lanza una excepción no detectada en C++, el tiempo de ejecución llama al std::terminate. Por defecto, std::terminate llama al std::abort que específicamente no llama a destructores en el camino de salida.

Con esta versión:

#include <iostream> 
using namespace std; 
struct C 
{ 
    C() { cout << __FUNCTION__ << endl; } 
    ~C() { cout << __FUNCTION__ << endl; } 
}; 

struct E: public C 
{ 
    C c; 
    E() { cout << __FUNCTION__ << endl; throw 4; } 
    ~E() { cout << __FUNCTION__ << endl; } 
}; 

int main() 
{ 
    try { 
     E e; 
    } catch(...) { 
    } 

    return 0; 
} 

recibo de salida:

C 
C 
E 
~C 
~C 
+0

eso es interesante. Pensé que C++ invocaría los destructores incluso si no se manejaban. – user236215

2

me he dado cuenta de que no llama destructor de la clase base o de sus miembros de datos si se produce una excepción desde el constructor

Sí, lo hace.

Sin embargo, ya que no catch esta excepción en todo el programa, el programa es inmediatamente terminada .

Si tuviera que atrapar la excepción en algún lugar más arriba de la pila de llamadas, los destructores de la clase base y los miembros se invocarían como se esperaba.

1

No maneja la "excepción".

> cat test.cpp 
#include <iostream> 

using namespace std; 
struct C 
{ 
    C() { cout << __FUNCTION__ << endl; } 
    ~C() { cout << __FUNCTION__ << endl; } 
}; 

struct E: public C 
{ 
    C c; 
    E() { cout << __FUNCTION__ << endl; throw 4; } 
    ~E() { cout << __FUNCTION__ << endl; } 
}; 

int main() 
{ 
    try 
    { 
     E e; 
    } 
    catch (int i) 
    { 
     std::cerr << "Handled " << i << std::endl; 
    } 
} 

Construir y ejecutar ..

> make test 
make: `test' is up to date. 
> ./test 
C 
C 
E 
~C 
~C 
Handled 4 
> 

Ambos C s destruidos y una terminación perfectamente normal.

1
1) E's constructor catched the exception and ran completly. 
    Therefore, its object is created and the distructor is 
    invoked. 

struct C 
{ 
    C() {cout <<__FUNCTION__<< endl;} 
    ~C() {cout <<__FUNCTION__<< endl;} 
}; 

struct E: public C 
{ 
    C c; 
    E() { 
    try { 
     cout <<__FUNCTION__<< endl; 
     throw 4; 
    } 
    catch(int i) { 
    cerr<<"int "<<i<<" is catched by "<<__FUNCTION__<<endl; 
    } 
} 
    ~E() {cout << __FUNCTION__ << endl;} 
void print(){ 
    cout<<"obj of class E is created"<<endl; 
} 
}; 

int main() 
{ 
    try { 
     E e; 
    e.print(); 
} 
catch(int i) { 
    cerr<<"int "<<i<<" catched by "<<__FUNCTION__<<" function"<<endl; 
    } 

    return 0; 
} 

/* 
Results: 
C::C 
C::C 
E::E 
int 4 is catched by E::E 
obj of class E is created 
E::~E 
C::~C 
C::~C 
*/ 

2) E's constructor didn’t catch the exception and ran incompletly. 
    In result, its object is not created. Therefore, its distructor 
    is not invoked. 

struct C 
{ 
    C() {cout <<__FUNCTION__<< endl;} 
    ~C() {cout <<__FUNCTION__<< endl;} 
}; 

struct E: public C 
{ 
    C c; 
    E() { 
    try { 
     cout <<__FUNCTION__<< endl; 
     throw 4; 
    } 
    catch(float i) { 
     cerr<<"int "<<i<<" is catched by "<<__FUNCTION__<<endl; 
    }  
} 
    ~E() {cout << __FUNCTION__ << endl;} 
void print(){ 
    cout<<"obj of class E is created"<<endl; 
} 
}; 

int main() 
{ 
    try { 
     E e; 
    e.print(); 
} 
catch(int i) { 
    cerr<<"int "<<i<<" catched by "<<__FUNCTION__<<" function"<<endl; 
    } 

    return 0; 
} 

/* 
Results: 
C::C 
C::C 
E::E 
C::~C 
C::~C 
int 4 catched by main function 
*/ 
Cuestiones relacionadas