如何在开关情况下消除对goto的使用
how to eliminate use of goto in switch case
基本上,我想接受来自用户的特定字符,然后使用开关大小写将与该字符大小写相关的字符串传递给另一个函数.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
[拼写错误]。此外,对于奖励点,如果您不关心性能影响,请考虑使用地图而不是开关。
拔示巴的建议是一个有效的替代方案,尽管我建议return
在switch
中看起来比continue
更清晰,因为后者在其他类型的控制流语句中有意义,而前者永远不会有意义。
还要注意的是,如果你出于某种充分的理由决定不使用某个函数,你的goto
实际上没有什么特别的问题,不要让货物崇拜者告诉你!
是的,有。由于continue
在switch
块中没有直接含义(参见。 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);
}
- 既然存在危险,为什么项目要使用-I include开关
- 为什么这个音频包络不能通过开关的情况?
- 有人知道为什么在开关中使用stoi函数会返回恒定的错误吗
- 在 ubuntu3 上C++ goto 定义有什么解决方案吗16.04?
- C 和 C++ 中开关语句的案例标签的常量值,但显示不同的行为
- 在 c++ 中在开关情况下使用和不使用"break"时的不同输出
- 为什么我的开关/机箱在使用枚举时默认?
- 如何正确指定 goto 语句的标签?
- 为什么布尔开关语句有编译器警告?
- 如何使用"equal to"以外的评估编写开关语句
- 为什么开关的优化方式与 c/c++ 中的链接不同?
- 无法找到简单的开关大小写枚举错误
- 未达到的情况会影响开关外壳性能
- ANTLR GOTO statement
- 为什么我的功能在使用 goto 时会给我带来"expected primary-expression before '}' token"?
- C++:我的开关盒循环转到第一种情况
- 有没有办法在C++将字符串与开关语句一起使用?
- 开关:无外壳中断
- 带有开关语句的 do-while 循环 -- 无穷循环错误
- 如何在开关情况下消除对goto的使用