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

时间:2014-04-07 10:47:51

标签: c++ switch-statement case default

通常在学校,我们的讲师会告诉我们在转换案例陈述的最后总是包含Default语句。但是,我一直想知道所有(或大多数)场景是否必要?

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

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

在上面的案例中,我觉得不可能有一个案例可以> 2或者< 0,所以我还需要包含Default语句吗?

在SO中有类似的问题,要求在{case}中使用Default。那里的回复表明,我们几乎应该在任何时候都包括Default。但在我个人遇到的所有情况中,它似乎是多余的,因为永远无法达到Default

编辑:此外,就防御性编程而言,此方案是否需要Default声明? 如果我要添加Default语句。它只是一个error-handling声明,我说得对吗?

6 个答案:

答案 0 :(得分:6)

从技术上讲,不,你没有,因为你已经用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

答案 1 :(得分:3)

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

答案 2 :(得分:2)

  

我还需要添加default声明吗?

只要假设num的可能范围成立,只要你不改变,

你就不会需要计算它的代码。

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

  

如果我要添加一个Default语句。它只是一个错误处理声明,我说得对吗?

是。我抛出logic_error,或者只是终止程序,以表明逻辑假设无效。

答案 3 :(得分:2)

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

例如,假设您想稍后添加一些案例/方法。如果按原样保留switch,则可能需要调试并查看未调用新方法的原因。另一方面,抛出一种NotImplementedException就会直接导致你忘记添加一个案例。

答案 4 :(得分:2)

根据具体情况,它可能是一种风格问题。我有时做的是以下几点。

考虑一下你在某些时候调整

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 :(得分:0)

因此,让我们稍微改进一下你的代码,让事情变得更加真实:

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