代码之家  ›  专栏  ›  技术社区  ›  user3437460

在这种情况下,我需要“默认”语句吗?

  •  4
  • user3437460  · 技术社区  · 10 年前

    通常在学校里,我们的讲师会告诉我们 Default switch-case语句末尾的语句。然而,我一直在想,对于所有(或大多数)场景来说,这是必要的吗?

    考虑C++中的以下示例:

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

    在上面的例子中,我觉得不可能有一个可以>2或<0,所以我仍然需要包含 违约 陈述

    SO中也有类似的问题,要求 违约 在开关情况下。那里给出的答复表明,我们几乎随时都应该包括 违约 。但在我个人遇到的所有案例中,这似乎是多余的,因为 违约 永远无法联系到。

    编辑 :此外,就防御性编程而言,此场景是否需要 违约 陈述 如果我要添加一个 违约 陈述它只会是一个 error-handling 声明,我这么说对吗?

    6 回复  |  直到 10 年前
        1
  •  6
  •   Karl Nicoll    10 年前

    从技术上讲,不,你没有,因为你的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 在生产过程中,你会遇到一个异常,而不仅仅是跑去做一些不应该发生的事情。

    编辑:

    我认为在防御性编程风格的基础上,加入一个包罗万象的功能是一个很好的补充。如果你错过了 案例 语句(即使有用的结果是导致程序崩溃)。

    编辑2:

    正如AndreKostur在对这个答案的评论中提到的,如果您打开枚举并忘记处理 案例 ,这是不包含 违约 枚举开关语句的大小写。提到 Phresnel's answer 了解更多信息。

        2
  •  3
  •   Mathias711    10 年前

    这不是必要的,但将其与印刷品一起使用是一个好习惯。我总是做一些像 print "This should NOT happen" 在默认情况下(或除此之外),所以我知道发生了一些我没有预料到的事情。电脑有时会做出奇怪的事情,请做好准备!

        3
  •  2
  •   anderas    10 年前

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

    例如,假设您希望稍后添加一些案例/方法。如果您离开 switch 实际上,您可能需要调试并了解为什么不调用新方法。另一方面 NotImplementedException 这将直接导致您忘记添加案例。

        4
  •  2
  •   Sebastian Mach    10 年前

    根据具体的上下文,这可能是一个风格问题。我有时会做以下事情。

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

    int num = rand()%3;
    

    int num = rand()%4;
    

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

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

    std::logic_error 是编程团队的错误。现在,如果您的团队忘记更新 switch ,它将(希望很早)有一个流产的程序,有一个可追踪的线索。

    的下侧 default

    包括 违约 -条款。当你 转换 enum ...

    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
  •  2
  •   Mike Seymour    10 年前

    我还需要包括 default 陈述

    你没有 需要 至,只要假设 num 只要你不改变计算它的代码,它就会保持不变。

    你可能会 希望 以验证该假设,以防将来发生变化。这种防御性编程可以在假设出现重大问题之前快速捕捉到错误的假设。

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

    对我会扔一个 logic_error ,或者可能只是终止程序,以指示逻辑假设无效。

        6
  •  0
  •   Andrey    10 年前

    因此,让我们将您的代码发展一点,使其更现实:

    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] 。您可以通过多种方式检查它,但您需要保护措施,并仔细检查输入并提出错误,即使它是内部功能。