2011-05-03 66 views
8

我创建了一个俄罗斯方块游戏,您可以在游戏结束后重新启动游戏。我用goto实现了这个快速和肮脏(见代码)。 Game类依赖于析构函数,这些调用与这些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 ...如果它仅仅是为转到 – 2011-05-03 16:18:35

+4

当然,你可以使用goto语句,如果你能承担后果:http://xkcd.com/292/ – bitmask 2011-05-03 16:37:33

回答

13

回答有关析构函数没有其他人似乎已经覆盖了问题。根据6.6/2,破坏者将为你调用。引用:

在退出范围(但是 成就),析构函数(12.4)被 要求的所有构造的对象 具有自动存储持续时间 (3.7.2)(命名对象或临时变量) 是在该范围内声明,在 他们 声明的顺序相反。调出一个循环, 的一个块外,或回过去具有自动 存储持续时间的 初始化变量涉及 破坏与 自动存储持续时间是在 范围在从 转印的点的变量,但不是在点转移到。

但是在这种情况下,我仍然没有建议goto。它不清楚(对我来说)表明发生了什么。你应该只使用while循环,而不是根据条件进行操作。

即使像这样简单的事情应该更清楚(尽管可能有一种方法可以在没有内部中断的情况下重写它)。这是十分明显的是,当地人清理while循环中使用这样的:

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回答析构问题的引用(我完全错过了......) – 2011-05-03 17:06:48

16

你可以轻松地在广大此功能在while循环,并设置标志来跳出来避免这种情况。

在C中,只有真正的“可接受的”使用goto是在错误的情况下跳跃到共同清理代码。在C++中,您甚至可以避免出现异常。所以真的,没有任何借口!

+0

我知道,但这感觉真的不合适,goto似乎更合适。这就是为什么我想知道这个goto是否可以接受的原因。 – orlp 2011-05-03 16:18:49

+1

@night:为什么一个循环看起来比一个快速又脏的'goto'更加不合适?你循环游戏,直到你不再想玩游戏。这对我来说似乎是完全自然的...... – 2011-05-03 16:20:00

+0

因为重启似乎意味着我们要回到代码的开头,这正是goto所做的。 – orlp 2011-05-03 16:21:16

2

GOTO语句是很少很好用。异常似乎是清理,你需要快速摆脱许多嵌套循环,释放一些内存并退出。这里可以很容易地用while循环替换。如果保持不变,只会使调试和维护变得更加困难。

-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

这更糟糕。休息,继续和转到都是“邪恶”的方式。 – 2011-05-03 16:23:43

5

将重要块分成函数,然后调用goto,而不是调用函数。

6

代替goto,您可以将您的新游戏标记中的所有内容放置到函数中while循环的结尾处。这个函数的返回值会告诉你是否必须重新运行。因此,这将是这样的:

... 
srand(time(NULL)); 

while (runGame()) 
{ 
} 

TTF_Quit(); 
... 

你将不得不通过runGame()从你的主要功能,你在你的游戏代码中使用任何参数和返回1,其中的代码使用转到和当它是最后一场比赛时为零。

0

有一些很好的时间使用goto(如:实现一个状态机),但我不知道这是真的其中之一。

如果是我,我把“游戏”的代码在一个子程序,退出了它完成的时候,然后让更高级别的常规选择开始新游戏什么的。