首先,我意识到从性能角度来看,这个switch语句的设计很慢,因为在某些情况下会多次调用cout。除此之外,这种编写switch语句的风格不是很好的编码实践。换句话说,最好是单独处理每个案例并更好地破坏或落后于什么?
int main(void)
{
int number;
cout << "Enter a number between 1 and 10 and I will display its Roman numeral equivalent." << endl
<< "> ";
cin >> number;
cout << "Roman numeral: ";
switch (number)
{
case 3:
cout << "I";
case 2:
cout << "I";
case 1:
cout << "I";
break;
case 4:
cout << "I";
case 5:
cout << "V";
break;
case 6:
cout << "VI";
break;
case 7:
cout << "VII";
break;
case 8:
cout << "VIII";
break;
case 9:
cout << "I";
case 10:
cout << "X";
break;
default:
cout << "Error!\nYou did not enter a number between 1 and 10";
}
cout << endl;
return 0;
}
答案 0 :(得分:4)
Switch语句并不慢,它们通常被编译器优化为跳转表。如果你确定switch语句按预期工作,那很好,这是一种非常酷的方式。
也就是说,如果您单独处理每个案件,您的案件数量会相同。如果这就是你所做的一切,我可能会改变它来单独处理每一个而不是堕落。它更容易理解,也很容易维护:
switch (number)
{
case 1:
cout << "I";
break;
case 2:
cout << "II";
break;
case 3:
cout << "III";
break;
case 4:
cout << "IV";
break;
case 5:
cout << "V";
break;
case 6:
cout << "VI";
break;
case 7:
cout << "VII";
break;
case 8:
cout << "VIII";
break;
case 9:
cout << "IX";
break;
case 10:
cout << "X";
break;
default:
cout << "Error!\nYou did not enter a number between 1 and 10";
}
和@paxdiablo建议的那样,为了提高可读性,如果它看起来更好,你可以将case,statement和break
全部放在同一行:
case 1: cout << "I"; break;
case 2: cout << "II"; break;
// etc.
答案 1 :(得分:1)
这是一个如此简单的案例,很难说它真的“糟糕”。我听说过有关租赁案件落入其他案件的不同意见。而且我确信在某些时候,我们都已经做到了。 (我通常用类似/ * FALL-THROUGH * /的方式清楚地记录它,所以下一个阅读代码的人知道我意味着这样做。)
我认为一个更复杂的例子会证明它并不是一个好主意。但事实并非如此,因为跌倒本身就是一件坏事。但是因为更好的设计不能保证。在面向对象的设计中,case语句可能表明你有一个“代码味道” - 你不是让对象根据类型而不是基于其他一些信息来做它所需要的东西。
现在,如果有人真的想对你公认的简单例子感到挑剔,可以说你正在以糟糕的方式混合控制器,模型和视图。您的控制器应该只是输入。您的模型将有更好的方法来获取输入的替代表示(地图,或者heck,case语句,我不知道),并且您的视图逻辑不会分散在控制器逻辑中和周围。通过实际遵循其他一些设计概念,switch语句可能会完全消失。其他例子也可能发生这种情况。
简而言之,我认为如果你的设计是合理的,你可能会发现,如果切换语句实际上是必要的,那么关于翻译案例陈述的关注并不是什么大问题。
答案 2 :(得分:1)
我倾向于不使用那种风格,因为通常很难一眼就找出控制流。这就是为什么goto
通常被认为是一个坏主意的一个原因(虽然有些人认为这是福音而不理解为什么 - 它在某些情况下实际上非常方便,只要它不会使代码不可读)。
换句话说,我更喜欢每个case
独立。如果它们具有共性,我将倾向于将其分解为单独的函数,并从每个案例中调用这些函数。
这不包括针对不同情况的代码相同的情况,其中我只使用类似(伪代码,显然)的东西:
case 1: case 2: case 3:
print "It's one, two or three"
对于特定的用例,我可能只会使用如下表格查找:
char *roman[] = {"I", "II", "III", "IV", ... "X"};
if ((n < 1) || (n > 10))
cout << "Urk! I only have ten fingers!";
else
cout << roman[n-1];
只是为了保持(源)代码紧凑。
答案 3 :(得分:0)
对每个案例使用Break应该是首选选项。使用“Fall-Through”将转移到下一个案例的代码,这可能会导致错误并阻碍性能。
答案 4 :(得分:0)
你是正确的,多次调用'&lt;&lt;' - 运算符会产生一定的开销。但是,你在这里谈论两个陈述,所以这可能不是优化的重点。
如果要优化代码,为什么不使用包含罗马数字的字符串的静态数组?像roman [0] =“I”,roman [1] =“II”等等。我怀疑这种表示会比上面的函数花费你更多的内存而且你摆脱了膨胀的switch语句。
答案 5 :(得分:0)
这非常聪明,而且速度很快。但如果你想改善表现......
cout
的字符串添加到正在运行的字符串缓冲区中。然后,在switch语句之后,cout
缓冲区。答案 6 :(得分:0)
编译器可以将切换语句优化为跳转表,因此它们并不总是很慢。而且他们肯定比编写一堆if
- else if
语句更好。我个人喜欢堕落,因为它允许你在某些情况下做一些很酷的东西,而不必重复代码;但总的来说,他们不赞成,因为他们比单独处理每个案件更难理解。
至于您的示例,如果您担心多次调用cout
,您始终可以将中间字符串存储在stringstream
中并打印最终字符串。但是,cout
的输出是缓冲的,因此我不知道这是否会有显着的性能提升。
#include <iostream>
#include <ios>
#include <sstream>
int main(void)
{
using namespace std;
int number = -1;
cout << "Enter a number between 1 and 10 and I will display its Roman numeral equivalent." << endl
<< "> ";
cin >> number;
ostringstream oss( "Roman numeral: ", ios_base::ate );
switch (number)
{
case 3:
oss << "I";
case 2:
oss << "I";
case 1:
oss << "I";
break;
case 4:
oss << "I";
case 5:
oss << "V";
break;
case 6:
oss << "VI";
break;
case 7:
oss << "VII";
break;
case 8:
oss << "VIII";
break;
case 9:
oss << "I";
case 10:
oss << "X";
break;
default:
cout << "Error!\nYou did not enter a number between 1 and 10";
return -1;
}
cout << oss.str() << endl;
return 0;
}