如何在开关情况下消除对goto的使用

how to eliminate use of goto in switch case

本文关键字:goto 开关 情况下      更新时间:2023-10-16

基本上,我想接受来自用户的特定字符,然后使用开关大小写将与该字符大小写相关的字符串传递给另一个函数.for。

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

如果用户打印了任何默认字符,那么它应该打印默认语句,并再次从用户那里获取字符并检查其大小写。我用goto做到了这一点,但是是否有任何其他选项可以避免或替换此代码中的goto

p:  
    {
        cout<<"Choose account type:-n"<<"Enterns :-savingnc :-currentnf :-fixednr :-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;
         }
     }
整个

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 typen";
    }
    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[拼写错误]。此外,对于奖励点,如果您不关心性能影响,请考虑使用地图而不是开关。

拔示巴的建议是一个有效的替代方案,尽管我建议returnswitch中看起来比continue更清晰,因为后者在其他类型的控制流语句中有意义,而前者永远不会有意义。

还要注意的是,如果你出于某种充分的理由决定不使用某个函数,你的goto实际上没有什么特别的问题,不要让货物崇拜者告诉你!

是的,有。由于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++。

使用布尔标志:

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

我建议将代码分成几个函数。这将使理解每个函数在做什么以及它是如何做的更容易理解的。

bool isValidAccountType(char c)
{
   return ( c == 's' || c == 'c' || c == 'f' || c == 'r');
}
char getAccountType()
{
   char c;
   cout<<"Choose account type:-n"<<"Enterns :-savingnc :-currentnf :-fixednr :-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);

如果将此代码放入函数中,则可以使用 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);

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

在这种情况下,将案例的处理单独移动到函数中并使用该函数的返回结果继续操作是正确的。

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

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

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

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"
             << "Enterns :-savingnc :-currentnf :-fixednr :-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);
}