如何消除在切换情况下使用goto

时间:2019-02-04 16:54:37

标签: c++

基本上我想从用户那里接受一个特定的字符,然后使用开关大小写将与该字符大小写有关的字符串传递给另一个函数。例如。

case i:strcpy(str,"ice-cream");
    other_function(str);
    break;

如果用户打印了任何默认字符,则应打印默认语句并再次从用户那里获取字符并检查其大小写。我使用goto进行了此操作,但是在此代码中还有其他选择可以避免或替换goto

p:  
    {
        cout<<"Choose account type:-\n"<<"Enter\ns :-saving\nc :-current\nf :-fixed\nr :-recurring"<<endl;
        char c;
        cin>>c;
        switch(c)
        {
            char t[20];
        case 's':
             strcpy(t,"saving");
             a[i].setype(t);
             break;
        case 'c':
             strcpy(t,"current");
             a[i].setype(t);
             break;
         case 'f':
             strcpy(t,"fixed");
             a[i].setype(t);
             break;
         case 'r':
             strcpy(t,"reccurring");
             a[i].setype(t);
             break;
         default:
             cout<<"Enter valid account type"<<endl;
             goto p;
         }
     }

6 个答案:

答案 0 :(得分:9)

应将整个switch分解为一个函数,并将其返回值用于确定接下来该循环将发生什么。

while (true) {
    std::cout << "Choose account type:\n" << std::flush;
    char c;

    if (cin >> c)
    {
       const bool result = HandleUserInput(a[i], c);
       if (result)
          break;
       else
          std::cout << "Enter valid account type\n";
    }
    else
    {
       // Input error - you'll want to do something about that
    }
}

并且:

// Given a character representing an account type, sets
// the type of 'account' as appropriate. Returns false
// on failure.
bool HandleUserInput(Account& account, const char c)
{
    switch (c)
    {
        case 's':
           a[i].setype("saving");
           return true;

        case 'c':
           a[i].setype("current");
           return true;

        case 'f':
           a[i].setype("fixed");
           return true;

        case 'r':
           a[i].setype("recurring");
           return true;

        default:
           return false;
    }
}

(请注意,我是如何摆脱strcpy的,这似乎不是必需的?取决于我猜想的setype [拼写错误]。此外,还有奖金要点,如果您不关心性能方面的问题,请考虑使用地图而非开关。)

Bathsheba的建议是一个有效的替代方法,尽管我建议return内的switch看起来比continue清晰得多,因为后者在其他类型的控件中具有含义流语句,而前者从不这样做。

还请注意,如果出于某些原因决定 not 不使用某个函数,那么您的goto其实并没有什么特别的错误,也不要让货运主义者告诉您否则!

答案 1 :(得分:7)

是的。由于continueswitch块中没有直接含义(请参见break),因此前者的存在将适用于适当的外循环控制结构。

所以……

do {
    // your code here, starting with "Choose account type".

    ...

    default:
        std::cout << "Enter valid account type" << std::endl;
        continue; // back to the start of the do loop
    } // close the switch
    break; // fall out of the loop
} while (true);

会做到这一点,并且是相当习惯的C ++。

答案 2 :(得分:3)

使用布尔值标志:

bool isInputSuccessful = false;
while (!isInputSuccessful)
{
    cout<<"Choose account type:-\n";
    char c;
    cin>>c;
    isInputSuccessful = true;
    switch(c)
    {
        char t[20];
        case 's':strcpy(t,"saving");
                 a[i].setype(t);
                 break;
        case 'c':strcpy(t,"current");
                 a[i].setype(t);
                 break;
        case 'f':strcpy(t,"fixed");
                 a[i].setype(t);
                 break;
        case 'r':strcpy(t,"reccurring");
                 a[i].setype(t);
                 break;
        default:cout<<"Enter valid account type"<<endl;
                 isInputSuccessful = false;
    }
}

在从用户输入之前,此代码将成功标志设置为true,如果未成功,则将其返回到false

或者,只要成功,就可以将其设置为true

答案 3 :(得分:2)

我建议将您的代码分成几个函数。这将使您更容易了解每个函数的功能以及其功能。

bool isValidAccountType(char c)
{
   return ( c == 's' || c == 'c' || c == 'f' || c == 'r');
}

char getAccountType()
{
   char c;
   cout<<"Choose account type:-\n"<<"Enter\ns :-saving\nc :-current\nf :-fixed\nr :-recurring"<<endl;
   while ( cin >> c )
   {
      if ( isValidAccountType(c) )
      {
         return c;
      }
      cout<<"Enter valid account type"<<endl;
   }

   // Wasn't able to get input.
   // Exit out of the program.
   exit(0);
}

void processAccount(char c)
{
   char t[20];
   switch(c)
   {
      case 's':strcpy(t,"saving");
               a[i].setype(t);
               break;
      case 'c':strcpy(t,"current");
               a[i].setype(t);
               break;
      case 'f':strcpy(t,"fixed");
               a[i].setype(t);
               break;
      case 'r':strcpy(t,"reccurring");
               a[i].setype(t);
               break;
      default:cout<<"Invalid account type"<<endl;
              return;
   }

   // Rest of the function.
}

main中使用以下内容。

char c = getAccountType();
processAccount(c);

答案 4 :(得分:1)

如果将此代码放入函数中,则可以使用return语句退出循环:

const char* enter() {
    for (;;) {
        std::cout << "Choose account type: ";
        char ch;
        std::cin >> ch;
        switch(ch) {
            case 's': return "saving";
            case 'c': return "current";
            case 'f': return "fixed";
            case 'r': return "recurring";
        }
        std::cout << "Invalid input.\n";
    }
}

现在您可以调用此函数并使用其结果:

char t[20];
strcpy(t, enter());
a[i].set_type(t);

答案 5 :(得分:-1)

尽管其他所有示例都非常有趣,但在可能的循环条件下,我通常会远离true

在这种情况下,将案件处理本身移到一个函数中并使用该函数的返回结果继续执行操作是正确的。

首先声明一些预定义的返回结果。

enum class Action_Result
{
    Ok,
    Error,
    Invalid_account,
    Quit
    /*...*/
};

接下来使函数返回预定义的结果。 (请注意,break代替了case中的return,用于分解函数并返回操作结果。

Action_Result handle_account_type(char c /*...*/)
{
    switch (c)
    {
        char t[20];
        case 's':
            strcpy(t, "saving");
            a[i].setype(t);
            return Action_Result::Ok;
        case 'c':
            strcpy(t, "current");
            a[i].setype(t);
            return Action_Result::Ok;
        case 'f':
            strcpy(t, "fixed");
            a[i].setype(t);
            return Action_Result::Ok;
        case 'r':
            strcpy(t, "reccurring");
            a[i].setype(t);
            return Action_Result::Ok;
        default:
            return Action_Result::Invalid_account;
    }
}

然后在主循环中,我们可以根据处理函数的结果进行决策。请注意,现在如何容易理解循环条件,为什么循环将继续执行以及何时停止循环。同样,所有输入和输出都在主要功能中,与动作分开(更好地遵循“单一职责原则”)。

int main()
{
    Action_Result account_handled_result = Action_Result::Error;

    do
    {
        cout << "Choose account type:-\n"
             << "Enter\ns :-saving\nc :-current\nf :-fixed\nr :-recurring"
             << endl;
        char c;

        if (cin >> c)
        {
            if (c == 'q')
                account_handled_result = Action_Result::Quit;
            else
                account_handled_result = handle_account_type(c);
        }
        else
        {
            account_handled_result = Action_Result::Error;
        }

        if (account_handled_result == Action_Result::Invalid_account)
             cout << "Enter valid account type" << endl;

    } while (account_handled_result != Action_Result::Quit);
}