【问题标题】:Do I need a "Default" statement in this scenario?在这种情况下我需要“默认”声明吗?
【发布时间】:2014-04-07 10:47:51
【问题描述】:

通常在学校,我们的讲师会告诉我们始终在 switch case 语句的末尾添加 Default 语句。但是,我一直想知道所有(或大多数)场景是否有必要?

考虑以下 C++ 示例:

int num = rand()%3;
switch (num)
{
   case 0: methodOne();
           break;
   case 1: methodTwo();
           break;
   case 2: methodThree();
           break;
}

上面的情况,我觉得不可能有可以>2或者Default声明吗?

在 SO 中有类似的问题,要求在 switch-case 中需要 Default。那里给出的答复表明,我们几乎在任何时候都应该包含Default。但在我个人遇到的所有案例中,这似乎是多余的,因为永远无法联系到Default

编辑:另外,就防御性编程而言,这种情况是否需要Default 声明? 如果我要添加 Default 声明。只会是error-handling 声明,我这样说对吗?

【问题讨论】:

  • 在这种特定情况下,没有必要包含 default 部分,因为正如您已经指出的那样,num 不可能有不同的值。
  • int i = 2; if (i == 2) { //...}。您需要另一个 else 语句吗?
  • 就像一个注释,n%3 有时可能是负数,但如果它来自 rand 则不是

标签: c++ switch-statement case default


【解决方案1】:

从技术上讲,不,你不需要,因为你已经用你的 switch 语句覆盖了所有可能的情况。

然而,无论如何,我总是发现在默认值中包含断言/异常很有用。考虑以下场景:

// V1.0.0: Initial version.
int num = rand()%3;
switch (num)
{
   case 0: methodOne();
           break;
   case 1: methodTwo();
           break;
   case 2: methodThree();
           break;
}

稍后...

// V1.0.0: Initial version.
// V1.0.1: Added a fourth method.
int num = rand()%4;
switch (num)
{
   case 0: methodOne();
           break;
   case 1: methodTwo();
           break;
   case 2: methodThree();
           break;
}

在这种情况下,开发人员 #2 更新了 rand 模数,但实际上并未添加大小写来处理 num == 4。如果没有default,您将默默地失败,这可能会导致各种难以调试的错误。一个更易于维护的解决方案可能是:

// V1.0.0: Initial version.
// V1.0.1: Added a fourth method.
int num = rand()%4;
switch (num)
{
   case 0: methodOne();
           break;
   case 1: methodTwo();
           break;
   case 2: methodThree();
           break;
   default:
           assert(false);
           throw InvalidNumException("BUG: Method has not been specified for value of num");
}

在调试时,这会在断言处停止调试器,如果(上帝保佑)丢失的case 一直到生产,你会抛出异常,而不仅仅是跑掉做事这不应该发生。

编辑:

我认为包含一个包罗万象是防御性编程风格的一个很好的补充。它保证如果您错过了 case 语句,您将获得有用的结果(即使该有用的结果会导致程序崩溃)。

编辑 2:

正如 Andre Kostur 在对此答案的评论中提到的那样,如果您打开枚举并忘记处理 case,某些编译器会发出警告,这是不包括 default 枚举的一个很好的理由。切换语句。有关详细信息,请参阅Phresnel's answer

【讨论】:

  • 这是最好的答案,因为在实际代码中防御性编程应该是强制性的,因为维护可能会将系统转换为其他东西。
  • 是和不是。如果我打开一个枚举,如果你没有覆盖 case 语句中的每个枚举值,各种现代编译器会警告你。通过提供默认子句,它将抑制此警告。这有助于未来的开发,就像你添加一个新的枚举值一样,编译器会告诉你需要调整的 switch 语句。
  • @AndreKostur - 你是绝对正确的。我打算编辑我​​的答案来谈论枚举,但我认为 phresnel 已经很好地涵盖了它,所以我认为它不值得重复。不过,我会添加一个指向我的答案的指针:-)
【解决方案2】:

这不是必需的,但将它包含在打印中是一个好习惯。我总是在默认(或除外)中执行print "This should NOT happen" 之类的操作,因此我知道发生了一些我没想到会发生的事情。计算机有时会做一些奇怪的事情,请做好准备!

【讨论】:

  • 维护注意事项。也许几年后会发生变化,导致新病例出现。
【解决方案3】:

严格来说,你没有。但是,在较大的项目中,如果您想稍后更改代码,它可以帮助您查找或避免错误。

例如,假设您想稍后添加一些案例/方法。如果您保留 switch 原样,您可能需要调试并查看为什么不调用新方法。另一方面,在那里扔一个NotImplementedException 会直接导致你忘记添加一个案例。

【讨论】:

    【解决方案4】:

    根据具体情况,这可能是风格问题。我有时会做以下事情。

    考虑到您在某个时间点进行了调整

    int num = rand()%3;
    

    int num = rand()%4;
    

    那么您的 switch 语句不再正确和完整。对于这种情况,您可以添加以下内容:

    default:
        throw std::logic_error("Oh noes.");
    

    std::logic_error 用于编程团队的错误。现在,如果您的团队忘记更新 switch,它将(希望早点)有一个中止程序,其中包含要寻找的踪迹。

    default 的缺点

    包含default 子句还有一个缺点。当你在switchenum ...

    enum class Color {
        Red, Green, Blue
    };
    
    ....
    
    Color c = ....;
    switch(c) {
    case Color::Red: break; 
    case Color::Green: break;
    };
    

    ... 一些编译器会警告您并非所有情况都包括在内。要关闭编译器,您现在可以做两件事:

    Color c = ....;
    switch(c) {
    case Color::Red: break;
    case Color::Green: break;
    default: break;
    };
    
    Color c = ....;
    switch(c) {
    case Color::Red:  break;
    case Color::Green:  break;
    case Color::Blue:  break;
    };
    

    您会意识到,在这两种选择中,后者可能更有效率。但是,这依赖于编译器行为。您仍然可以在默认情况下抛出异常,但将一个不错的编译时错误转换为运行时错误,其中许多人认为前者更可取。

    可以使用提前退出或结构来实现两全其美(可移植错误,以及编译时错误):如果遇到案例,您可以在之后进行测试:

    Color c = ....;
    switch(c) {
    case Color::Red: return;
    case Color::Green: return;
    };
    throw std::logic_error(...);
    
    
    MYSQL mysql = {0};
    switch(c) {
    case Color::Red: mysql = red_database(); break;
    case Color::Green: mysql = green_database(); break;
    };
    if (!mysql)
        throw std::logic_error(...);
    

    【讨论】:

      【解决方案5】:

      我还需要包含default 声明吗?

      您不需要需要,只要关于num 的可能范围的假设成立 - 只要您不更改计算它的代码,它就会这样做。

      您可能想要验证该假设,以防将来发生变化。这种防御性编程有助于在错误的假设引起重大问题之前快速发现它们。

      如果我要添加默认语句。这只是一个错误处理语句,我这样说对吗?

      是的。我会抛出一个logic_error,或者可能只是终止程序,以表明逻辑假设是无效的。

      【讨论】:

      • 好吧,实际上用户会问关于 num 的可能范围的假设是否成立
      • @lizusek:不是这样;但我已经澄清它确实成立。
      【解决方案6】:

      所以让我们稍微改进一下你的代码,让它变得更真实:

      void processNumber(int maxNum) {
         int num = rand()%maxNum;
         switch (num)
         {
            case 0: methodOne();
                 break;
            case 1: methodTwo();
                 break;
            case 2: methodThree();
                 break;
         }
      }
      

      在这里您需要确保它在允许值集合[1, 2, 3] 中。您可以通过多种方式检查它,但您需要安全措施并仔细检查输入并引发错误,即使它是内部函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-01
        • 1970-01-01
        • 2015-10-11
        • 1970-01-01
        相关资源
        最近更新 更多