2011-05-03 11 views
8

Creé un juego de tetris donde puedes reiniciar después de un juego terminado. Implementé esto rápido y sucio con un goto (ver código). La clase Game se basa en destructores, ¿estos se llaman con estos goto? ¿Qué tan malo es este goto, es aceptable, o qué debo hacer en su lugar?¿Qué tan malo es este goto?

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { 
    // initiate sdl 
    sdl_init(); 

    // seed rng 
    srand(time(NULL)); 

    newgame: // new game label 
    Game game(GAME_WIDTH, GAME_HEIGHT, 1, screen); 

    // keydowns 
    bool fastfall = false; 
    bool gamerunning = true; 
    Uint32 lastupdate = 0; 

    while (gamerunning && game.isalive()) { 
     // game running stuff here 
    } 

    // game over stuff here 

    while (gamerunning) { 
     if (SDL_PollEvent(&event)) { 
      if (event.type == SDL_QUIT) { 
       gamerunning = false; 
      } else if (event.type == SDL_KEYDOWN) { 
       if (event.key.keysym.sym == SDLK_r) goto newgame; // yay a new game! 
      } 
     } 
    } 

    TTF_Quit(); 
    SDL_Quit(); 
    return 0; 
} 
+4

Ewww ... si sólo fuera por el Goto –

+4

Por supuesto, se puede utilizar un Goto, si se puede vivir con las consecuencias : http://xkcd.com/292/ – bitmask

Respuesta

13

Para responder a la pregunta sobre los destructores que nadie más parece haber cubierto. De acuerdo con 6.6/2, se llamará a los destructores . Cita:

En la salida de un ámbito de aplicación (sin embargo consumado), destructores (12.4) están llamada para todos los objetos construidos con una duración de almacenamiento automático (3.7.2) (nombrado objetos o temporales) que son declaró en ese alcance, en el orden inverso de su declaración . Traslado de salida de un bucle, de un bloque, o hacia atrás más allá una variable inicializada con duración automática almacenamiento implica la destrucción de variables con duración de almacenamiento automático que se encuentran en alcance en el punto transferido de pero no en el punto transferido a.

Sin embargo, todavía no sugiero goto en este caso en absoluto. No es claro (para mí de todos modos) indicar lo que está sucediendo. Debería usar un ciclo while y hacer que opere en las condiciones en su lugar.

Incluso algo tan simple como esto debería ser más claro (aunque probablemente haya una forma de reescribirlo sin la ruptura interna). Es perfectamente obvio que la gente del lugar se limpian utilizado dentro de un bucle while así:

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { 
    // initiate sdl 
    sdl_init(); 

    // seed rng 
    srand(time(NULL)); 

    bool gamerunning = true; 
    while(gamerunning) 
    { 
     Game game(GAME_WIDTH, GAME_HEIGHT, 1, screen); 

     // keydowns 
     bool fastfall = false; 
     Uint32 lastupdate = 0; 

     while (gamerunning && game.isalive()) { 
      // game running stuff here 
     } 

     // game over stuff here 

     while (gamerunning) { 
      if (SDL_PollEvent(&event)) { 
       if (event.type == SDL_QUIT) { 
        gamerunning = false; 
       } else if (event.type == SDL_KEYDOWN) { 
        if (event.key.keysym.sym == SDLK_r) break; // yay a new game - get out of the "what to do next" loop. 
       } 
      } 
     } 
    } 

    TTF_Quit(); 
    SDL_Quit(); 
    return 0; 
} 
+3

+1 para la cita que responde a la pregunta de destructor (que pude perder por completo ...) –

16

Desde aquí se puede evitar esto poniendo la mayor parte de esta función en un bucle while, y el establecimiento de una bandera para salir de ella.

En C, el único uso real "aceptable" de goto fue para saltar al código de limpieza común en el caso de errores. En C++, puede evitar incluso esto con excepciones. Entonces realmente, ¡no hay excusa!

+0

Lo sé, pero esto se siente realmente fuera de lugar, un goto parece más apropiado. Es por eso que quiero saber si este goto es aceptable. – orlp

+1

@night: ¿Por qué un loop parece estar más fuera de lugar que un 'goto' rápido y sucio? Le das vueltas a los juegos, hasta que ya no quieras jugar. Eso parece completamente natural para mí ... –

+0

Porque un reinicio parece implicar que estamos volviendo al principio del código, que es exactamente lo que hace un goto. – orlp

2

Gotos rara vez son buenos para usar. La excepción parece ser la limpieza, donde necesita romper rápidamente muchos bucles anidados, liberar algo de memoria y salir. Esto aquí puede reemplazarse fácilmente con un ciclo while. Si se deja como está, solo hará que la depuración y el mantenimiento sean más difíciles.

-1
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { 
    // initiate sdl 
    sdl_init(); 

    // seed rng 
    srand(time(NULL)); 


    while (1) { 
    Game game(GAME_WIDTH, GAME_HEIGHT, 1, screen); 

    // keydowns 
    bool fastfall = false; 
    bool gamerunning = true; 
    Uint32 lastupdate = 0; 

    while (gamerunning && game.isalive()) { 
     // game running stuff here 
    } 

    // game over stuff here 
    restart_game = false; 
    while (gamerunning) { 
     if (SDL_PollEvent(&event)) { 
      if (event.type == SDL_QUIT) { 
       gamerunning = false; 
      } else if (event.type == SDL_KEYDOWN) { 
       if (event.key.keysym.sym == SDLK_r) { 
         restart_game = true; break; 
       } 
      } 
     } 
    } 
    if (!restart_game) break; 
    } 

    TTF_Quit(); 
    SDL_Quit(); 
    return 0; 
} 
+0

Esto es aún peor. Los descansos, los continuos y los goto son todos "malvados" de la misma manera. –

5

romper los bloques significativos arriba en funciones y luego en lugar de llamar goto, a llamar a una función en su lugar.

6

En lugar de una goto puedes poner todo desde tu newgame etiqueta hasta el final del ciclo while en una función. El valor de retorno de esta función le dirá si tiene que volver a ejecutar. Por lo que sería algo así como:

... 
srand(time(NULL)); 

while (runGame()) 
{ 
} 

TTF_Quit(); 
... 

Usted tendría que pasar runGame() cualquier parámetro de su función principal que utiliza en su código de juego y devolver un 1 en el que el código utiliza el Goto y un cero cuando es el último juego.

0

Hay algunos buenos momentos para usar goto (por ej .: Implementación de una máquina de estado), pero no estoy seguro de que este sea realmente uno de ellos.

Si fuera yo, colocaría el código del "juego" en una subrutina, saldré de él cuando termine y luego dejaré que la rutina de nivel superior elija comenzar un juego nuevo o algo así.

Cuestiones relacionadas