c++ 代码忽略用户输入并随机吐出稍后打印的截断字符串

rslzwgfq  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(84)

我试图调用一个函数,它将从一个表中随机生成一个字符串。(忽略奇怪的目的,我正在寻找一个挑战,我的朋友建议这一点)当函数被调用时,它忽略所有的'cin'并吐出这个响应。烤面包机死亡总数是:C:\Users\MH\source\repos\suicidefun\x64\bug\suicidefun.exe(进程25852)退出代码0。
我的代码是这样的:

// imports
#include <iostream>
using namespace std;

//var
string methods[21] = { "Hanging", "Shooting", "Poisoning", "Drinking Bleach", "Toaster Bath", "something idk", "Eating something you are allergic to", "Fall damage", "Mr. Hands", "Stabbing", "Shoving a pointy object in you", "Thinking you are in a dream and doing something stupid","Drunk driving","Overdose","Drowning","Fire","Starving or dehydrating","Driving off a cliff","Driving into a family of five's ford fiesta","Eat glass","Suicide by cop" };
time_t current_time = time(NULL);
int random;
bool again = true;
int killcount = 0;
string x = "N/A";
int check = 0;
bool valid = false;
char ans = 'n';

void thing(){   
    srand((unsigned)time(NULL));
    int random = rand() % 20;
    cout << methods[random];
    killcount++;
    while (valid = false) {
        cout << "Kill yourself again? (y/n)";
        cin >> x;
        if (x != "y" || "n" || "Y" || "N") {
            cout << "invalid answer";
        }
        else {
            valid = true;
            break;
        }
    }
    if (x == "y") {
        cout << "Current death total is:" + killcount;
    }
    else if (x == "Y") {
        cout << "Current death total is:" + killcount;
    }
    else {
        again = false;
        cout << "Final death total is:" + killcount;
    }
}

int main() {
    thing();
    return 0;
}

字符串
我期望发生的是输出一个随机字符串,变量numbercount增加1,程序询问用户是想继续还是停止,然后程序输出numbercount的值,如果用户选择停止,则再次将布尔值设置为false。

nfg76nw0

nfg76nw01#

以下是我在你的程序中发现的一些问题。

避免using namespace std;

一般来说,专业程序员不会使用using namespace std;。您也不应该使用。相反,只需键入std::作为您需要从C++标准库使用的任何名称的前缀。例如,std::cout
using namespace std是一个using directive。@Chris在下面的评论中指出,* 使用声明 *,如using std::swapusing std::cout,是一个不同的故事(如果它们被明智地使用)。见下文。在这些情况下,使用swapcout没有前缀可能是可以的。

全局变量

在一个大型程序中,使用“全局”变量是有问题的,因为很难跟踪它们在哪里被修改了。证明程序正确性(对于所有执行路径)几乎是不可能的。更糟糕的是,当你有一个bug时,因为你的一个全局变量得到了错误的值,很难跟踪它在哪里出错了。
在下面的示例程序中,我将变量设置为“local”。

赋值与相等

在C++中,* 赋值 * 使用=,而测试 * 相等 * 使用==

// This is wrong:
while (valid = false) ...

// This is correct:
while (valid == false) ...

// This is even better:
while (valid) ...

字符串
根据其本身,valid将是truefalse。但测试valid == true的答案也是如此,因此最佳做法是单独使用变量。
赋值表达式valid = false将变量valid设置为false,* 然后将false返回到周围的表达式。* 因此,while (valid = false)while(false)相同。

重复比较

对于重复比较,您必须为每次比较重新声明变量名。

// This is wrong:
if (x != "y" || "n" || "Y" || "N") ...

// This is correct:
if (x != "y" && x != "n" && x != "Y" && x != "N") ...


请注意,您必须使用&&加入测试。在伪代码中,这将给您给予:
如果x不是“y”且x不是“n”且x不是“Y”且x不是“N”...

不能使用+连接带有数字的字符串

在C++中,不能使用+将字符串文字与数字连接起来。

// This is wrong:
cout << "Current death total is:" + killcount;

// This is correct:
cout << "Current death total is: " << killcount;

// So is this:
std::string s = "Current death total is: " + std::to_string(killcount);


你的程序“吐出截断字符串”的原因是因为像"Current death total is:" + killcount这样的表达式是有效的 * 指针表达式 *。例如,当killcount是3时,它返回一个指向"Current death total is:"中第4个字符的指针。当该指针使用std::cout输出时,结果是"rent death total is:"

缺少外循环

函数thing缺少一个应该由变量again控制的外部循环。

void thing(){   
    srand((unsigned)time(NULL));
    bool again;
    do {
        // play game ...
    } while (again);
    std::cout << "Final death total is: " << killcount << "\n\n";
}

随机数

函数rand使用的算法 * 依赖于实现。* 每个编译器都定义了自己的版本。有些编译器使用好的算法,而有些则没有。同时,在C11中引入的Mersenne twister引擎std::mt19937要求在所有编译器上都相同。
即使rand在你的编译器上是可以的,rand() % 20肯定不是。它生成的随机数不是均匀分布的。某些值比其他值返回得更频繁。(参见pigeonhole_ principle。)这就是为什么std::uniform_int_distribution被添加到C
11中。它返回的数字保证是均匀分布的。
我通常将std::mt19937std::uniform_int_distribution封装到一个简单的类中,比如下面的类RNG。这样,我就可以为我需要的每种随机数拥有一个单独的成员函数。然而,对于你的游戏,我只需要一个这样的函数:random_method

class RNG
{
    std::mt19937 mt{ std::random_device{}() };
    std::uniform_int_distribution<std::size_t> dist;
    using param_type = std::uniform_int_distribution<std::size_t>::param_type;
public:
    auto random_method(std::size_t size) {
        return dist(mt, param_type{ 0, size - 1 });
    }
};


这里有一些关于RNG类的注意事项。

  • 在C++中,std::size_t是数组下标使用的类型。这就是为什么我用std::size_t示例化std::uniform_int_distribution。我希望它返回一个随机下标。
  • std::random_device{}()返回一个随机的32位种子,std::mt19937的构造函数将其用作种子。
  • param_type{ 0, size - 1 }构造一个param对象,该对象包含std::uniform_int_distribution对象dist将返回的最小值和最大值。
  • dist需要两个参数来生成一个随机数:第一个是随机数引擎mt,第二个是一个param对象,它给出了要使用的数字范围。

调用函数获取键盘输入

一般来说,我每次需要从std::cin获取输入时都会调用一个函数。这里有一个简短的函数,它从用户那里获取Y/N响应,同时拒绝无效的响应。
它使用std::getline,所以当你把它和std::cin >> value;这样的语句混合在一起时必须小心,因为std::cin >> value;在输入流上留下了一个挂起的'\n'。你必须在调用get_yes_no之前去掉它,通常是通过调用std::cin.ignore。然而,这在这里不是问题。因为没有像std::cin >> value;这样的东西在任何地方都使用。

bool get_yes_no(
    std::string const& prompt,
    std::istream& ist = std::cin,
    std::ostream& ost = std::cout)
{
    std::string s;
    for (;;)
    {
        if (!prompt.empty())
            ost << prompt;
        else 
            ost << "Yes or No? (y/n): ";
        if (!std::getline(ist, s))
            throw std::runtime_error(
                "ERROR: `std::getline` failed unexpectedly.");
        s = to_upper(trim_whitespace(s));
        if (s == "Y" || s == "YES")
            return true;
        if (s == "N" || s == "NO")
            return false;
        ost << "Invalid entry. Please reenter.\n\n";
    }
}

Helper函数

get_yes_no调用一对辅助函数。第一个将字符串转换为大写。

std::string to_upper(std::string s) noexcept
{
    std::transform(s.begin(), s.end(), s.begin(),
        [](unsigned char c) { return std::toupper(c); }
    );
    return s;
}


第二个函数从字符串中删除前导和尾随空格。

auto trim_whitespace(std::string const& s) -> std::string
{
    // Trim leading and trailing whitespace from string `s`.
    auto const first{ s.find_first_not_of(" \f\n\r\t\v")};
    if (first == std::string::npos)
        return {};
    auto const last{ s.find_last_not_of(" \f\n\r\t\v") };
    enum : std::string::size_type { one = 1u };
    return s.substr(first, (last - first + one));
}

简化游戏功能

有了这些辅助函数,程序的代码就简化成这样了。

void kill_game()
{
    char const* methods[] = {
        "Hanging"
        , "Shooting"
        , "Poisoning"
        , "Drinking Bleach"
        , "Toaster Bath"
        , "something idk"
        , "Eating something you are allergic to"
        , "Fall damage"
        , "Mr. Hands"
        , "Stabbing"
        , "Shoving a pointy object in you"
        , "Thinking you are in a dream and doing something stupid"
        , "Drunk driving"
        , "Overdose"
        , "Drowning"
        , "Fire"
        , "Starving or dehydrating"
        , "Driving off a cliff"
        , "Driving into a family of five's ford fiesta"
        , "Eat glass"
        , "Suicide by cop"
    };
    RNG rng;
    int kill_count{};
    std::cout 
        << "Welcome to the Suicide Game!\n\n"
        << "How do you want to kill yourself this time?\n";
    do {
        std::cout
            << '\n'
            << methods[rng.random_method(std::size(methods))]
            << "\nCurrent death total is: " 
            << ++kill_count
            << "\n\n";
    } while (get_yes_no("Kill yourself again? (y/n): "));
    std::cout << "\n\nFinal death total is: " << kill_count << "\n\n";
}


下面是main函数:

// main.cpp
#include <algorithm>
#include <cctype>
#include <cstddef>
#include <iostream>
#include <random>
#include <string>

// Class `RNG` ...

// Function `to_upper` ...

// Function `trim_whitespace` ...

// Function `get_yes_no` ...

// Function `kill_game` ...

int main() {
    kill_game();
    return 0;
}
// end file: main.cpp

示例运行(输入错误)

示例运行的输出:

Welcome to the Suicide Game!

How do you want to kill yourself this time?

Drunk driving
Current death total is: 1

Kill yourself again? (y/n):
Invalid entry. Please reenter.

Kill yourself again? (y/n): garbage keystrokes
Invalid entry. Please reenter.

Kill yourself again? (y/n): y

Starving or dehydrating
Current death total is: 2

Kill yourself again? (y/n): y

Fall damage
Current death total is: 3

Kill yourself again? (y/n): n

Final death total is: 3

相关问题