这个goto有多糟糕?

时间:2011-05-03 16:15:38

标签: c++ restart goto

我创建了一个俄罗斯方块游戏,您可以在游戏结束后重新启动。我用goto实现了这个快速和脏(见代码)。 Game类依赖于析构函数,这些是用这些goto调用的吗?这个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;
}

7 个答案:

答案 0 :(得分:16)

您可以通过将此函数的大部分放在while循环中,并设置一个标志来突破它来轻松避免这种情况。

在C中,goto唯一真正“可接受”的用途是在出现错误时跳转到常见的清理代码。在C ++中,除了异常之外,你甚至可以避免这种情况。真的,没有任何借口!

答案 1 :(得分: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;
}

答案 2 :(得分:6)

您可以将 newgame 标记中的所有内容放入函数中while循环的末尾,而不是goto。此函数的返回值将告诉您是否必须再次运行。所以它会是这样的:

...
srand(time(NULL));

while (runGame())
{
}

TTF_Quit();
...

您必须传递 runGame()您在游戏代码中使用的主函数中的任何参数,并返回1,其中代码使用 goto 和a当它是最后一场比赛时为零。

答案 3 :(得分:5)

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

答案 4 :(得分:2)

Gotos很少使用。异常似乎是清理,你需要快速打破许多嵌套循环,释放一些内存并退出。这里可以很容易地用while循环替换。如果保留原样,只会使调试和维护变得更难。

答案 5 :(得分:0)

使用goto有一些好时机(例如:实现状态机),但我不确定这是否真的是其中之一。

如果是我,我会把“游戏”代码放在一个子程序中,完成后退出它,然后让更高级别的例程选择开始一个新游戏。

答案 6 :(得分:-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;
}