问题描述

我最近开始从事我的代码的清洁/物流工作,如果你能给我一些关于如何改进某些事情的技巧,我将不胜感激。

这是一个简单的控制台 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 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。