2010-03-09 17 views
12

Me pregunto, para ningún otro propósito que pura curiosidad (porque nadie DEBERÍA escribir código así) sobre cómo el comportamiento de RAII se acopla con el uso de goto (preciosa idea no es) .¿Qué sucede cuando combinamos RAII y GOTO?

class Two 
{ 
public: 
    ~Two() 
    { 
     printf("2,"); 
    } 
}; 

class Ghost 
{ 
public: 
    ~Ghost() 
    { 
     printf(" BOO! "); 
    } 
}; 

void foo() 
{ 
    { 
     Two t; 
     printf("1,"); 
     goto JUMP; 
    } 
    Ghost g; 
JUMP: 
    printf("3"); 
} 

int main() 
{ 
     foo(); 
} 

Cuando ejecuto el siguiente código en Visual Studio 2005 obtengo el siguiente resultado.

1,2,3 BOO! 

Sin embargo, me imaginaba, adivinado, la esperanza de que 'BOO!' en realidad no aparecería como Ghost nunca debería haberse instanciado (en mi humilde opinión, porque no sé el comportamiento real esperado de este código).

¿Qué pasa?


me he dado cuenta de que si una instancia de un constructor explícito para Ghost el código no compila ...

class Ghost 
{ 
public: 
    Ghost() 
    { 
     printf(" HAHAHA! "); 
    } 
    ~Ghost() 
    { 
     printf(" BOO! "); 
    } 
}; 

Ah, el misterio ...

+1

Creo que el comportamiento es correcto. De lo contrario, ¿cómo podría referirse a la variable g después de JUMP? – leiz

+2

http://xkcd.com/292/ –

Respuesta

23

Las conversaciones sobre estándares esto explícitamente - con un ejemplo; 6.7/3 "Declaración de declaración" (énfasis añadido por mí):

Las variables con duración de almacenamiento automática se inicializan cada vez que se ejecuta su instrucción de declaración. Las variables con la duración de almacenamiento automática declarada en el bloque se destruyen al salir del bloque.

Es posible transferir a un bloque, pero no de una manera que evite las declaraciones con la inicialización. Un programa que salta desde un punto donde una variable local con una duración de almacenamiento automática no está en el alcance hasta un punto donde está en el alcance está mal formado a menos que la variable tenga un tipo de POD y se declare sin un inicializador.

[Ejemplo:

void f() 
{ 
    //... 
    goto lx; //ill-formed: jump into scope of a 
    //... 

ly: 
    X a = 1; 
    //... 

lx: 
    goto ly; //OK, jump implies destructor 
       //call for a, followed by construction 
       //again immediately following label ly 
} 

-fin ejemplo]

Así que me parece que el comportamiento de MSVC no es compatible con las normas - Ghost no es un tipo POD, por lo que el compilador debe emitir una error cuando la declaración goto está codificada para pasarla.

Un par de otros compiladores que probé (GCC y Digital Mars) emiten errores. Comeau emite una advertencia (pero para ser justos, mi script de compilación para Comeau lo ha configurado para una alta compatibilidad con MSVC, por lo que podría estar siguiendo la iniciativa de Microsoft intencionalmente).

+1

¡Gracias por encontrar el lugar donde esto está definido en el estándar! Sin embargo, me pregunto si mal formado significa que debe o no debe compilar ... –

+0

"Mal formado" significa que el programa no está "bien formado". Los compiladores solo deben aceptar y "ejecutar correctamente" los programas que están bien formados. Es decir, si está "mal formado", es un error. – greyfade

+0

@Robert: Agregué algunas palabras sobre el comportamiento de MSVC aquí, como debería haber hecho inicialmente. –

0

Goto no es radiactivo. Irse de aquí para allá es algo diferente de partir por excepción. Ingresar por goto debe ser dictado por conveniencia, no por los límites del idioma. No saber si el fantasma está construido o no es una buena razón para no hacer eso.

Salta antes que el constructor. Si desea saltar después de que algún objeto ya está construido, enciérrelo en un nuevo ámbito o resuelva su vida usted mismo.

0

En este escenario, he encontrado útil el siguiente enfoque.

void foo() 
{ 
    { 
     Two t; 
     printf("1,"); 
     goto JUMP; 
    } 

    { 
     Ghost g; 
     // operations that use g. 
    } 

// g is out of scope, so following JUMP is allowed. 
JUMP: 
    printf("3"); 
} 

limitando al alcance de la variable g en su función foo(), hará que el salto Goto legal. Ahora, no saltamos de un lugar donde g no se inicializa a un lugar donde se espera que g se inicialice.

Cuestiones relacionadas