当前位置:  开发笔记 > 编程语言 > 正文

切换if-else语句的优点

如何解决《切换if-else语句的优点》经验,为你挑选了7个好方法。

使用switch语句与使用if30个unsigned枚举的语句的最佳实践是什么,其中大约10个具有预期的操作(目前是相同的操作).需要考虑性能和空间,但并不重要.我已经抽象了代码片段,所以不要因为命名惯例而讨厌我.

switch 声明:

// numError is an error enumeration type, with 0 being the non-error case
// fire_special_event() is a stub method for the shared processing

switch (numError)
{  
  case ERROR_01 :  // intentional fall-through
  case ERROR_07 :  // intentional fall-through
  case ERROR_0A :  // intentional fall-through
  case ERROR_10 :  // intentional fall-through
  case ERROR_15 :  // intentional fall-through
  case ERROR_16 :  // intentional fall-through
  case ERROR_20 :
  {
     fire_special_event();
  }
  break;

  default:
  {
    // error codes that require no additional action
  }
  break;       
}

if 声明:

if ((ERROR_01 == numError)  ||
    (ERROR_07 == numError)  ||
    (ERROR_0A == numError)  || 
    (ERROR_10 == numError)  ||
    (ERROR_15 == numError)  ||
    (ERROR_16 == numError)  ||
    (ERROR_20 == numError))
{
  fire_special_event();
}

Nils Pipenbr.. 152

使用开关.

在最坏的情况下,编译器将生成与if-else链相同的代码,因此您不会丢失任何内容.如果有疑问,请将最常见的案例放在switch语句中.

在最好的情况下,优化器可能会找到更好的方法来生成代码.编译器所做的常见事情是构建二进制决策树(在一般情况下保存比较和跳转)或者只是构建一个跳转表(根本没有比较).



1> Nils Pipenbr..:

使用开关.

在最坏的情况下,编译器将生成与if-else链相同的代码,因此您不会丢失任何内容.如果有疑问,请将最常见的案例放在switch语句中.

在最好的情况下,优化器可能会找到更好的方法来生成代码.编译器所做的常见事情是构建二进制决策树(在一般情况下保存比较和跳转)或者只是构建一个跳转表(根本没有比较).


jakoben:可以这样做,但仅适用于类似开关的if/else链.实际上,这些都不会发生,因为程序员使用switch.我深入研究编译器技术并相信我:找到这样"无用"的构造需要花费很多时间.对于编译器人来说,这样的优化确实很有意义.
@NilsPipenbrinck易于在模板元编程中构建伪递归的`if`-`else`链,以及生成`switch``case`链的难度,该映射可能变得更加重要.(是的,古老的评论,但网络是永远的,或至少到下周二)
请注意,理论上可以将一系列ifs分析出来与编译器的切换相同,但为什么要抓住机会呢?通过使用开关,您可以准确地传达您想要的内容,这可以使代码生成更容易.

2> Mark Ransom..:

对于您在示例中提供的特殊情况,最清晰的代码可能是:

if (RequiresSpecialEvent(numError))
    fire_special_event();

显然,这只会将问题移到代码的不同区域,但现在您有机会重用此测试.您还有更多选择如何解决它.你可以使用std :: set,例如:

bool RequiresSpecialEvent(int numError)
{
    return specialSet.find(numError) != specialSet.end();
}

我并不是说这是RequiresSpecialEvent的最佳实现,只是它是一个选项.您仍然可以使用开关或if-else链,或查找表,或对值进行一些位操作,无论如何.您的决策过程越模糊,您在孤立函数中获得的价值就越大.


这是真的.可读性比switch和if语句都要好得多.我自己实际上会回答这样的问题,但是你打败了我.:-)

3> paercebal..:

该开关更快.

只需在循环中尝试if/else-ing 30个不同的值,并使用开关将其与相同的代码进行比较,以查看开关的速度.

现在,交换机有一个真正的问题:交换机必须在编译时知道每种情况下的值.这意味着以下代码:

// WON'T COMPILE
extern const int MY_VALUE ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

不会编译.

然后大多数人将使用定义(Aargh!),其他人将在同一编译单元中声明和定义常量变量.例如:

// WILL COMPILE
const int MY_VALUE = 25 ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

因此,最终,开发人员必须在"速度+清晰度"与"代码耦合"之间进行选择.

(并不是说开关不能被写成令人困惑的地狱......我目前看到的大多数开关都是这个"令人困惑"的类别"......但这是另一个故事......)

编辑2008-09-21:

bk1e添加了以下注释:" 将常量定义为头文件中的枚举是另一种处理此问题的方法".

当然如此.

extern类型的要点是将值与源分离.将此值定义为宏,作为简单的const int声明,或者甚至作为枚举具有内联值的副作用.因此,如果define,enum值或const int值发生变化,则需要重新编译.extern声明意味着在价值变化的情况下不需要重新编译,但另一方面,使得无法使用开关.结论是使用开关将增加开关代码和用作情况的变量之间的耦合.如果是,则使用开关.如果不是,那就不足为奇了.

.

编辑2013-01-15:

Vlad Lazarenko评论了我的答案,给出了他对交换机生成的汇编代码的深入研究的链接.非常enlightning:http://741mhz.com/switch/


切换[不__always__更快](http://lazarenko.me/2013/01/13/switch-statement-machine-code/).

4> Alexandra Fr..:

编译器无论如何都会对它进行优化 - 选择最易读的开关.


有可能编译器不会触及if-then-else.事实上,`gcc`肯定不会那样做(这是有充分理由的).Clang将两种情况都优化为二进制搜索.例如,请参见[this](http://lazarenko.me/2013/01/13/switch-statement-machine-code/).

5> scubabbl..:

Switch,如果只是为了可读性.在我看来,巨大的if语句难以维护且难以阅读.

ERROR_01://故意落空

要么

(ERROR_01 == numError)||

后者更容易出错,需要比第一次更多的打字和格式化.



6> Bdoserror..:

可读性代码.如果您想知道哪些性能更​​好,请使用分析器,因为优化和编译器各不相同,性能问题很少出现在人们认为的情况下.



7> Martin Becke..:

使用开关,它是它的用途和程序员的期望.

我会把冗余的案例标签放进去 - 只是为了让人们感觉舒服,我试图记住什么时候/什么规则让他们离开.
你不希望下一个编程人员不得不对语言细节做任何不必要的思考(可能是你几个月后!)

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有