通常在学校,我们的讲师会告诉我们在转换案例陈述的最后总是包含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
声明,我说得对吗?
答案 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
- 条款也有不利之处。当您switch
时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 :(得分: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]
的集合中。您可以通过多种方式检查它,但是您需要保护措施并仔细检查输入并引发错误,即使它是内部功能。