2011-05-03 64 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(如:實現一個狀態機),但我不知道這是真的其中之一。

如果是我,我把「遊戲」的代碼在一個子程序,退出了它完成的時候,然後讓更高級別的常規選擇開始新遊戲什麼的。