Насколько это плохо? - PullRequest
       40

Насколько это плохо?

8 голосов
/ 03 мая 2011

Я создал игру в тетрис, которую можно перезапустить после окончания игры. Я реализовал это быстро и грязно с помощью 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;
}

Ответы [ 7 ]

16 голосов
/ 03 мая 2011

Этого можно легко избежать, поместив большую часть этой функции в цикл while и установив флаг, чтобы выйти из нее.

В C единственное реальное "приемлемое" использование goto предназначался для перехода к общему коду очистки в случае ошибок.В C ++ вы можете избежать даже этого с исключениями.Так что на самом деле, нет оправдания!

13 голосов
/ 03 мая 2011

Чтобы ответить на вопрос о деструкторах, никто, похоже, еще не рассмотрел.Согласно 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;
}
6 голосов
/ 03 мая 2011

Вместо goto вы можете поместить все от вашего тега newgame до конца цикла while в функции.Возвращаемое значение этой функции сообщит вам, нужно ли вам снова запускать.Так что это будет что-то вроде:

...
srand(time(NULL));

while (runGame())
{
}

TTF_Quit();
...

Вам нужно будет передать runGame () любые параметры из вашей основной функции, которые вы используете в коде игры, и вернуть 1, где кодиспользует goto и ноль, когда это последняя игра.

5 голосов
/ 03 мая 2011

Разбейте значимые блоки на функции и затем вместо вызова goto просто вызовите функцию.

2 голосов
/ 03 мая 2011

Gotos редко хороши для использования. Кажется, что исключение касается очистки, когда вам нужно быстро вырваться из множества вложенных циклов, освободить память и выйти. Это можно легко заменить циклом while. Если оставить его как есть, это только усложнит отладку и обслуживание.

0 голосов
/ 03 мая 2011

Есть хорошие времена для использования goto (например: реализация конечного автомата), но я не уверен, что это действительно один из них.

Если бы это был я, я бы поместил код "игры" в подпрограмму, вышел из нее, когда закончил, а затем разрешил подпрограмме более высокого уровня начать новую игру или что-то в этом роде.

0 голосов
/ 03 мая 2011
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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...