这有多糟糕

How bad is this goto?

本文关键字:      更新时间:2023-10-16

我创造了一款俄罗斯方块游戏,你可以在游戏结束后重新开始。我用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;
}

您可以通过将该函数的大部分放在while循环中,并设置标志以跳出该循环来轻松避免这种情况。

在C语言中,goto唯一真正"可接受"的用途是在出现错误时跳转到常见的清理代码。在c++中,甚至可以通过异常来避免这种情况。所以,真的,没有借口!

要回答关于析构函数的问题,似乎没有人涉及到。根据6.6/2,析构函数将为您调用。引言:

在退出作用域时(然而完成),析构函数(12.4)是调用所有构造对象具有自动保存期限(3.7.2)(命名对象或临时对象)在作用域中声明的它们的倒序声明。转移出一个循环,走出一个街区,或者回到过去用automatic初始化变量储存时间包括变量的销毁自动存储的持续时间从点转移的范围而不是在点上转移到。

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

即使像这样简单的东西也应该更清楚(尽管可能有一种不需要内部break的重写方法)。很明显,局部变量在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;
}

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

...
srand(time(NULL));
while (runGame())
{
}
TTF_Quit();
...

你必须传递runGame()你在游戏代码中使用的main函数的任何参数,并返回一个1当代码使用goto和一个0当它是最后一场比赛

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

Gotos很少好用。清理似乎是个例外,在这种情况下,您需要快速跳出许多嵌套循环,释放一些内存并退出。这里可以很容易地用while循环代替。如果保持原样,只会使调试和维护更加困难。

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

如果是我,我会把"游戏"代码放在子程序中,完成后退出,然后让更高级的程序选择开始一个新游戏或其他东西。

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;
}
相关文章:
  • 没有找到相关文章