关于正确设计程序C ++的困惑

时间:2011-03-04 18:30:04

标签: c++

我制作了一个小程序来生成素数并让用户检查一个数字,看看它是不是素数。问题是,我不确定如何正确设计它。这是该计划:

#include <iostream>
#include <vector>

typedef unsigned long long bigint;

std::vector<bool> sieve(size_t size)
{   
    std::vector<bool> primelist(size);

    primelist[0] = false;
    primelist[1] = false;

    for (bigint i = 2; i < size; ++i) { primelist[i] = true; }

    for (bigint i = 2; i * i < size; ++i)
    {
        if (primelist[i])
        {
            for (bigint j = i; j * i < size; ++j)
                primelist[i*j] = false;
        }
    }

    return primelist;
}

int main()
{
    bigint range;
    bigint number;
    std::vector<bool> primes;

    std::cout << "Enter range: " << std::endl;
    std::cin >> range;

    primes = sieve(range);

    while (1)
    {
        std::cout << "Enter number to check: " << std::endl;
        std::cin >> number;

        if (primes[number])
            std::cout << "Prime" << std::endl;

        else
            std::cout << "Not prime" << std::endl;
    }

    return 0;
}

我想要实现的基本流程是:输入范围,/句柄输入/,要检查的输入数字,/句柄输入/

我还希望通过编写“更改范围数字”等命令,为用户提供在任何给定时间更改范围的选项

我有一些问题:

如果用户输入大于无符号long long的范围,并且用户基本上超过任何限制(例如,如果他输入的范围是100,那么如果他检查101),我希望程序受到控制异常将被捕获。我知道这需要使用try / catch / throw来实现,但我不知道如何在保持选项更改范围的同时不使用我的代码意大利面代码。

另外,我希望错误是枚举类型(我读到枚举对异常有好处),比如

enum errors
{
    OUT_OF_RANGE = 1,    //Out of the range specified by the user
    INCORRECT_VALUE,    //If user input "one" instead of 1
    RANGE_SIGNED,     //If user inputs a signed value for range
    NUM_LIMITS        //Number exceeds unsigned long long
};

我不知道如何使用异常处理,更不用说在枚举中使用它了。我该如何保持这个程序的安全和运行,同时远离意大利面条代码?

我非常困惑。如果有人可以帮我正确设计这个程序并保持可读性和效率,那么它将真正改善我未来的程序设计。

感谢阅读!

3 个答案:

答案 0 :(得分:1)

你问了很多。

您想验证用户输入。用户不应该输入大数字,非整数等。

我将首先回答这绝对不是应该使用例外的情况。例外情况用于处理异常情况。这些是你无法预料或真正处理的。

用户输入的数字太大了?你可以处理。告诉他们他们的号码太大,请输入介于1和X之间的数字。

用户输入单词apple?你可以处理。告诉他们他们只能输入整数。

这样做的一种方法是制作ValidateInput函数。你可以让它返回一个数字(或枚举,它们基本上是一样的)来告诉你是否有错误。

为了进行验证,您很可能必须以std::string的形式接收输入,然后在将其转换为数字之前对其进行验证。将输入作为unsigned int或类似的整数类型实际上不允许您检查错误。

这增加了一些工作,因为您需要手动手动验证输入。有一些库可以帮助解决这个问题,例如boost::lexical_cast,但这对你来说可能太过分了。

下面是一些非常基本的伪代码来说明我的意思。它只是为了让您知道该做什么,它不会编译或为您完成工作。您可以通过创建一个基于错误代码等返回消息的泛型函数来进一步扩展它。

enum error_code {
  SUCCESS,          // No error
  OUT_OF_RANGE,     // Out of the range specified by the user
  INCORRECT_VALUE,  // If user input "one" instead of 1
  RANGE_SIGNED,     // If user inputs a signed value for range
  NUM_LIMITS        // Number exceeds unsigned long long
};

// This function will check if the input is valid.
// If it's not valid, it will return an error code to explain why it's invalid.
error_code ValidateInput(const std::string& input) {
  // Check if input is too large for an unsigned long long
  if (InputIsTooLarge)
    return NUM_LIMITS;
  // Check if input is negative
  if (InputIsNegative)
    return RANGE_SIGNED;
  // Check if input is not an integer
  if (InputIsNotInteger)
    return INCORRECT_VALUE;
  // If we make it here, no problems were found, input is okay.
  return SUCCESS;
}

unsigned long long GetInput() {
  // Get the user's input
  std::string input;
  std::cin >> input;

  // Check if the input is valid
  error_code inputError = ValidateInput(input);

  // If input is not valid, explain the problem to the user.
  if (inputError != SUCCESS) {
    if (inputError == NUM_LIMITS) {
      std::cout << "That number is too big, please enter a number between " 
        "1 and X." << std::endl;
    }
    else if (inputError == RANGE_SIGNED) {
      std::cout << "Please enter a positive number." << std::endl;
    }
    else if (inputError == INCORRECT_VALUE) {
      std::cout << "Please enter an integer." << std::endl;
    }
    else {
      std::cout << "Invalid input, please try again." << std::endl;
    }

    // Ask for input again
    return GetInput();
  }
  // If ValidateInput returned SUCCESS, the input is okay.
  // We can turn it into an integer and return it.
  else {
    return TurnStringIntoBigInt(input);
  }
}

int main() {
  // Get the input from the user
  unsigned long long number = GetInput();

  // Do something with the input
}

答案 1 :(得分:1)

我喜欢Dauphic的答案,特别是因为它说明了将问题分解成各个部分并单独解决它们。但是,我会GetInput做一点不同的事情:

unsigned long long GetInput() {
  // Get the user's input
  std::string input;

  error_code inputError;
  // Repeatedly read input until it is valid
  do {
    std::cin >> input;
    inputError = ValidateInput(input);

    if (inputError == NUM_LIMITS) {
      std::cout << "That number is too big, please enter a number between " 
        "1 and X." << std::endl;
    }
    // ...handle all other cases similarly
  } while(inputError != SUCCESS);

  // If ValidateInput returned SUCCESS, the input is okay.
  // We can turn it into an integer and return it.
  return TurnStringIntoBigInt(input);
}

递归解决方案很好,但是有一个缺点,那就是递归并增加堆栈。在这种情况下,这可能不是什么大问题,但需要注意的是。

至于如何编写ValidateInput,基本上你将扫描字符串中的无效字符,如果没有找到,则测试该值是否适合您选择的整数类型,直到将其读入变量用例如>>

注意:此解决方案存在严重缺陷,因为它不会检查std::cin的状态。如果用户要通过EOF,即按^ D,程序将陷入循环,这不是好行为。

答案 2 :(得分:0)

你最好使用bitset而不是bool的矢量 有了它,您可以使用Eratosthene方法来确定数字是否为素数。