問題描述

我最近開始從事我的程式碼的清潔/物流工作,如果你能給我一些關於如何改進某些事情的技巧,我將不勝感激。

這是一個簡單的控制檯 Hangman 遊戲我做了:

#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <time.h>
#include <vector>

using std::cout; using std::cin;
using std::endl; using std::string;
using std::vector; using std::ifstream;

void Clear_Screen(){
    cout << string(1000, 'n');
}

vector<string> Add_Lines(unsigned short int usiLength){
    vector<string> vecsLines_Vector;
    for(unsigned short int i = 0; i < usiLength; i++){
        vecsLines_Vector.push_back("_");
    }
    return vecsLines_Vector;
}

string Output_Guesses(vector<string> vecsVector, unsigned short int usiLimit){
    string sOutput;
    for(unsigned short int usiI = 0; usiI < usiLimit; usiI++){
        sOutput += vecsVector[usiI] + " ";
    }
    return sOutput;
}

int main(){

    while(true){

        cout << "t" << " __   __  _______  __    _  _______  __   __  _______  __    _ " << endl
             << "t" << "|  | |  ||   _   ||  |  | ||       ||  |_|  ||   _   ||  |  | |" << endl
             << "t" << "|  |_|  ||  |_|  ||   |_| ||    ___||       ||  |_|  ||   |_| |" << endl
             << "t" << "|       ||       ||       ||   | __ |       ||       ||       |" << endl
             << "t" << "|       ||       ||  _    ||   ||  ||       ||       ||  _    |" << endl
             << "t" << "|   _   ||   _   || | |   ||   |_| || ||_|| ||   _   || | |   |" << endl
             << "t" << "|__| |__||__| |__||_|  |__||_______||_|   |_||__| |__||_|  |__|" << endl << endl

                             << "tttt" << "---------------" << endl
                             << "tttt" << "| 1. New Game |" << endl
                             << "tttt" << "| 2. Exit     |" << endl
                             << "tttt" << "---------------" << endl
                             << "tttt" << "Selected : ";

            // Prevents from entering an invalid value
            unsigned short int usiMenu_Select = 0;
            do{
                cin >> usiMenu_Select;
            }while(usiMenu_Select < 1 || usiMenu_Select > 2);

            // Quit Game
            if(usiMenu_Select == 2){
                    return 0;
            }
            Clear_Screen();

            // Grab all the words from the dictionary and store them into a vector
            vector<string> vecsWords;
            unsigned int uiLength = 0;

            ifstream Dictionary;
            Dictionary.open("dictionary.txt");
            if(Dictionary.is_open()){
                while(Dictionary.good()){
                    string sWord;
                    getline(Dictionary, sWord);
                    vecsWords.push_back(sWord);
                    uiLength++;
                }
            }
            Dictionary.close();

            // Pick a random word from the dictionary vector
            srand (time(NULL));
            const unsigned int kuiRandom_Number = rand() % uiLength;
            const string ksWord_Selected = vecsWords[kuiRandom_Number];

            // Creates guessing lines
            const unsigned short int kusiWord_Length = ksWord_Selected.length();
            const unsigned short int kusiTries = 6;
            vector<string> vecsGuesses = Add_Lines(kusiWord_Length);
            vector<string> vecsWrongs = Add_Lines(kusiTries);

            // Guessing game loop
            unsigned short int usiIncorrect = 0;
            unsigned short int usiCorrect = 0;
            while(usiIncorrect < kusiTries){

                cout << "ttt" << "Correct : " << Output_Guesses(vecsGuesses, kusiWord_Length) << endl << endl <<
                "ttt" << "  Wrong : " << Output_Guesses(vecsWrongs, kusiTries) << endl << endl <<
                "ttt" << "  Guess : ";

                // Prevents from entering more than 1 character
                string cGuess;
                do{
                    cin >> cGuess;
                }while(cGuess.length() > 1);

                // Checks if letter guessed is part of the word
                bool bValid = false;
                for(unsigned short int i = 0; i <= kusiWord_Length; i++){
                    const string ksValid_Character = ksWord_Selected.substr(i,1);
                    if(cGuess == ksValid_Character){
                        vecsGuesses[i] = cGuess;
                        usiCorrect++;
                        bValid = true;
                    }
                }

                // Checks if answer is valid
                if(!bValid){
                    vecsWrongs[usiIncorrect] = cGuess;
                    usiIncorrect++;
                }

                // Output end game message
                if(usiCorrect == kusiWord_Length){
                    cout << "ttt" << " _ _ _ _                 " << endl
                         << "ttt" << "| | | |_|___ ___ ___ ___ " << endl
                         << "ttt" << "| | | | |   |   | -_|  _|" << endl
                         << "ttt" << "|_____|_|_|_|_|_|___|_|  " << endl
                         << "ttt" << "Enter any keys to continue..";
                         cin >> cGuess;
                         usiIncorrect = kusiTries;
                }else if(usiIncorrect == kusiTries){
                    cout << "ttt" << " __                    " << endl
                         << "ttt" << "|  |   ___ ___ ___ ___ " << endl
                         << "ttt" << "|  |__| . |_ -| -_|  _|" << endl
                         << "ttt" << "|_____|___|___|___|_|  " << endl
                         << "ttt" << "Enter any keys to continue..";
                         cin >> cGuess;
                         usiIncorrect = kusiTries;
                }
                Clear_Screen();
            }
    }
}

最佳解決思路

  • 您可以按字母順序或按組分組您的 STL #include(在不同的答案中為 @Loki Astari 提供信用):

    // This is Class.cpp
    #include "Class.h"
    #include "OtherMyClassIdependon1.h"
    #include "OtherMyClassIdependon1.h"

    // Now C++ header files // Lots of people order these alphabetically
    #include <string> // Personally I group them
    // All the containers together.
    // All the streams together.
    // etc. Each group alphabetical.

    // Now C header files // Lots of people order these alphabetically
    #include <unistd.h>

    // Now System header files
    #include <ICantThinkOfAnything>

  • 使用<ctime> 代替<time.h> 。後者是 C 庫,位於 namespace std 內。

  • 那些 using 在頂部看起來很粘。我只是把 std:: 放在需要的地方。為了可維護性和 name-clashing 的避免,這也將有助於將 STL 實體與其他實體分開。

  • 如果大寫字母用於型別,函式應以小寫字母開頭。

    使用 camelCase:

    void oneFunction() {}
    

    使用 snake_case:

    void another_function() {}
    
  • Output_Guesses()不改變容器,所以透過 vecsVector const&

    std::string Output_Guesses(std::vector<std::string> const& vecsVector, /* ... */) {}
                                                     // ^^^^^^
    
  • std::srand()移動到 main()的頂部,以實現可維護性,並將其改寫為:

    // use nullptr instead if you're using C++11
    std::srand(static_cast<unsigned int>(std::time(NULL)));
    

    沒有這個,std::srand()每次返回相同的隨機數。如果您的編譯器發出警告,則只需要轉換,因此請確保它們已啟用。

    另外,如果你有 C++ 11,用 nullptr 替換 NULL

  • 當處理 STL 容器 (如 std::vectorstd::string),use their iterators instead of indices whenever possible 。一次發生在 Add_Lines()

  • 對於您的”winner” 和”loser” 輸出,您正在重新整理每行的緩衝區,這是 std::endl 正在做的。要做一個換行符,不要這樣做,新增 n 在引號內的任何地方。您仍然可以將 std::endl 放在程式的最後一行或需要重新整理的地方。

  • while (true)沒問題另一個選項是 for (;;),它也直到停止 (通常使用 break) 進行無限迴圈。

  • 我會考慮使用 typedef 為您的 frequently-used 型別,如 const unsigned short int 。例如:如果所有字長都使用相同的型別,則將其命名為 WordLength 。這是大寫的,因為它是一個 user-defined 型別。關於字長,喜歡 std::string::size_type 。如果您使用的超級 freakishly-large 字串超出了 unsigned short int 的大小,該怎麼辦?使用這種型別將保證您可以使用這麼長的字,而無需更改所有這些型別。

  • 常數可以放在全球範圍內:

    const unsigned short int kusiTries = 6
    

    這是可以的,因為它不是 const 的全域性變數,不能在程式的其他地方修改。

    此外,請勿使用 strongly-typed 語言 (如 C++) 中的匈牙利符號。只需一個簡單的 tries 即可。

  • 使用所有的”magic numbers”,您都可以在 main()中使用 EXIT_SUCCESSEXIT_FAILURE 。對於”magic numbers” 本身,更喜歡常數 (如上所述) 。

  • 為了正確輸入 std::string,最好選擇 std::getline()

    getline(std::cin, cGuess);
    
  • 對於輸入驗證,如選單,我建議 exception-handling(try /catch) 。這將阻止程式從 bugging-up 輸入不良,並允許恢復。

  • 最後 (也許最重要的是):使用更多的功能。您可能仍然保持程式簡單,而不使用類,沒關係。但是,當您將所有內容都填入 main()(或任何一種功能) 時,可以顯著降低程式的整體可維護性。只要在 main()中保持”welcome” 的內容,選單和遊戲狀態 (繼續播放) 。其他一切都應該根據目的分為功能。建立一個只顯示簡單的訊息的函式也不實用,除非它發生需要引數。此外,如果您重複使用相同的程式碼塊 (例如顯示陣列),請將其放入單獨的函式中。

參考文獻

注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。