2010-10-25 19 views
6

Tengo un objeto C++ simple que creo al comienzo de la función F() para asegurar que se invocan dos funciones coincidentes (OpDo, OpUndo) al inicio y el retorno de la F() , utilizando el constructor y el destructor del objeto. Sin embargo, no quiero que la operación se deshaga en caso de que se haya lanzado una excepción dentro del cuerpo de F(). ¿Es esto posible hacerlo limpiamente? He leído sobre std :: uncaught-exception, pero su uso no parece ser recomendable.Cómo detectar el desenrollado de la pila en un Destructor

+0

Usar'uncaught_exception' podría estar bien, pero depende de lo que esté haciendo (y por lo tanto es subjetivo ...). Sin embargo, ¿podría explicar un poco más sobre la operación do/undo y la (s) excepción (es)? – Macke

+3

Si no desea que suceda una acción cuando se propone una excepción, simplemente no use RAII. Simplemente coloque el OpDo() y OpUndo() directamente en el código. –

+1

"su uso no parece ser recomendado", porque no funciona como un medio para detectar si el bit de pila que contiene su objeto se está desenrollando debido a una excepción o debido a un retorno normal. Supongamos que alguien pone un código en un destructor que llama a su función F. Supongamos además que dicho objeto se destruye como parte del desenrollado de la pila durante una excepción. Entonces 'uncaught_exception' devolverá' true', pero no se ha lanzado ninguna excepción dentro del cuerpo de F(). –

Respuesta

6

La mayoría de las personas han usado std::uncaught_exception() para tratar de saber si hay una excepción pendiente, por lo que pueden lanzar una excepción desde un destructor si no hay uno ya. Eso generalmente se considera No una Buena Idea.

Si no desea deshacer una operación si se ha lanzado una excepción, debería funcionar.

Recuerde que el destructor es su última oportunidad de liberar los recursos que tiene un objeto, porque una vez que el destructor finaliza, el objeto no existe y todos los recursos que posee se filtran de forma permanente. Si OpDo() asigna cualquier memoria o manejadores de archivos o lo que sea, debe tratar eso en el destructor pase lo que pase.

0

Vamos a suponer que su F vuelve alguna clase auxiliar:

Helper F() 
{ 
    MyClass doUndoWrapper; 
} 

Cuando el flujo es normal - se crea ayudante. Cuando se produce una excepción, no se crea una copia de Helper. Intenta usar esta semántica al ubicar al constructor de la región privada de Helper y declarar a F como amigo, para que nadie pueda crear ayuda.

class Helper 
{ 
private: 
    friend Helper F(); 
    Helper(){ //place there OpDo semantic - first entry 
       // construct this class 
    Helper(const Helper& copy){ //this must present to allow stack operations 
       // copy constructor will be called at end of `F` to return value 
       // so place OpUndo semantic there to mark success without exception 
+0

¿Qué? Esa no es una respuesta muy clara en absoluto. –

+0

@C Johnson - Si usa 'Helper' como valor de retorno obtiene los beneficios follow (1) Helper se crea al principio según se requiera (así que coloque OpDo en el constructor) (2) Puede reconocer si la función F ha regresado exitosamente - el constructor de copia llamado (así que coloque OpUndo allí) (3) en caso de excepción no se llama a ningún constructor de copia. Para aclarar esto, estoy cambiando el código para reflejar esta idea. – Dewfy

0

Puede subvertir el Scope Guard idiom. En lugar de no hacer algo en el destructor cuando se produce ninguna excepción, invertimos eso y única hacer algo si se lanza una excepción:

class DoUndoRAII{ 
public: 
    DoUndoRAII() 
    : noexcept_(false) 
    { 
    // your stuff here 
    } 

    ~DoUndoRAII(){ 
    if(noexcept_){ 
     // do whatever you need to do 
    } 
    } 

    void no_exception(){ 
    noexcept_ = true; 
    } 

private: 
    bool noexcept_; 
}; 

void func(){ 
    DoUndoRAII do_undo; 

    // last line 
    do_undo.no_exception(); 
} 

Cuando se produce una excepción, do_undo.no_exception() nunca serán llamados y por lo tanto, nunca establezca el valor noexcept_ en verdadero. :) Se puede encontrar un ejemplo here on Ideone.

Cuestiones relacionadas