关于使用 switch 语句的问题

Posted

技术标签:

【中文标题】关于使用 switch 语句的问题【英文标题】:Question about using a switch statement 【发布时间】:2011-09-10 00:59:39 【问题描述】:

首先,我意识到从性能的角度来看,这个 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;

【问题讨论】:

【参考方案1】:

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.

【讨论】:

对于像这样的简单案例,我会将案例、cout 和 break 放在一行中,并将它们排成三列(默认除外)——我讨厌不得不查看大块代码来解决问题:-) 当然,这不会使您的答案无效,这只是一种编码风格。 谢谢赛斯。永远感谢您的意见! :)【参考方案2】:

这样一个简单的案例,很难说它真的很“糟糕”。我听说过关于让案件落入其他案件的不同意见。我敢肯定,在某些时候,我们都做到了。 (我通常用 /* FALL-THROUGH */ 之类的东西清楚地记录它,所以下一个阅读代码的人知道我打算这样做。)

我认为一个更复杂的例子会证明这不是一个好主意。但并不是真的,因为跌倒本身就是一件坏事。但是因为更好的设计并不保证它。在面向对象的设计中,case 语句可能表明你有一种“代码味道”——你没有让对象根据类型而不是基于其他一些信息来做它需要做的事情。

现在,如果有人真的想对您公认的简单示例挑剔,可能会说您正在以糟糕的方式将控制器、模型和视图混合在一起。您的控制器应该简单地获取输入。您的模型将有更好的方法来获取输入的替代表示(我不知道一个地图,或者见鬼,一个案例陈述),并且您的视图逻辑不会分散在您的控制器逻辑中和周围。通过实际遵循其他一些设计概念,switch 语句可能会完全消失。其他示例也可能发生这种情况。

简而言之,我认为如果你的设计是合理的,你可能会发现如果 switch 语句实际上是必要的,那么对失败的 case 语句的关注并不是什么大问题。

【讨论】:

我喜欢阅读这篇文章。就像我为其他人所说的那样,我从这里的社区学到了很多东西。【参考方案3】:

我倾向于不使用这种风格,因为通常很难一目了然地弄清楚控制流程。这就是为什么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];

只是为了保持(源)代码紧凑。

【讨论】:

你的例子很干净。我真的很喜欢看其他人是如何做事的,因为它让我看到了我没有想到的方法。但是,我正在学习的 c++ 类希望我们使用 switch 语句。所以当我写它的时候,我意识到失败会适用于一些条目。 (: @fhaddad78:如果是上课,我怀疑你的教育者可能会因为失败而皱眉,因为他们认为可读性远比“聪明”的编码实践更重要。请记住这一点。 如果讲师失败或依赖于他们给出的指导方针中有效的任何实施,他们是一个糟糕的讲师,应该报告。教师可以在事后评论可能“更好”的方法以及为什么 - 这就是指导。但是惩罚不违反准则和工作的事情是不可接受的。【参考方案4】:

对每种情况使用 Break 应该是首选选项。使用“Fall-Through”将转到下一个案例的代码,这可能会导致错误并影响性能。

【讨论】:

【参考方案5】:

你是对的,多次调用'

如果你想优化代码,为什么不使用包含罗马数字的静态字符串数组呢?像 roman[0] = "I"、roman[1] = "II" 等。我怀疑这种表示会比上面的函数消耗更多的内存,并且你摆脱了臃肿的 switch 语句。

【讨论】:

是的,这是一种更好的方法,但是,对于我正在上的这门课,我们必须使用 switch 语句。这是一个介绍性的 c++ 编程课程。【参考方案6】:

这非常聪明,而且速度很快。但是如果你想提高性能...

在每个 switch 语句中,将您正在 couting 的字符串添加到正在运行的字符串缓冲区中。然后,在 switch 语句之后,cout 缓冲区。 这有点让人难以阅读/理解。简单地单独处理这 10 个案例中的每一个会更简单,并且性能会更好(尽管使用了微不足道的更多内存)。

【讨论】:

cout 已被缓冲。为什么您认为添加另一层缓冲会使其更快?【参考方案7】:

编译器可以将切换语句优化为跳转表,因此它们并不总是很慢。而且它们绝对比写一堆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;

【讨论】:

我喜欢最后一个 cout 的想法。 (:这个问题有很多很酷的想法。

以上是关于关于使用 switch 语句的问题的主要内容,如果未能解决你的问题,请参考以下文章

关于switch case语句的问题

关于 Java switch 语句 - 在每种情况下使用 return 并省略中断

C语言关于SWITCH语句的跳转问题

使用带有 AVQueuePlayer 的 Switch 语句?

关于if与switch的使用与区别

关于switch中case的用法!