C++;长切换语句还是使用映射查找? 在C++应用程序中,我有一些值作为代码来表示其他值。为了翻译代码,我一直在争论使用switch语句还是stl映射。开关的外观如下所示: int code; int value; switch(code) { case 1: value = 10; break; case 2: value = 15; break; }

C++;长切换语句还是使用映射查找? 在C++应用程序中,我有一些值作为代码来表示其他值。为了翻译代码,我一直在争论使用switch语句还是stl映射。开关的外观如下所示: int code; int value; switch(code) { case 1: value = 10; break; case 2: value = 15; break; },c++,dictionary,switch-statement,std,stdmap,C++,Dictionary,Switch Statement,Std,Stdmap,映射将是一个stl::map,而转换将是一个简单的查找,使用代码作为键值 哪一个更好/更有效/更干净/被接受?为什么?如果您所做的只是赋值,我会说map。我的理由是它是可扩展的,无需更改代码,而switch语句则不是 顺便说一句,如果代码足够连续并且其范围允许,那么使用老式的直接数组会更好,比如 int lookup[] = {-1, 10, 15, -1 222}; 然后,switch语句可以被重写为 值=查找[代码] 所有其他选项都会在一定程度上增加成本。我认为,如果“代码”的数量变大,则

映射将是一个
stl::map
,而转换将是一个简单的查找,使用代码作为键值


哪一个更好/更有效/更干净/被接受?为什么?

如果您所做的只是赋值,我会说map。我的理由是它是可扩展的,无需更改代码,而switch语句则不是


顺便说一句,如果代码足够连续并且其范围允许,那么使用老式的直接数组会更好,比如

int lookup[] = {-1, 10, 15, -1 222};
然后,switch语句可以被重写为

值=查找[代码]


所有其他选项都会在一定程度上增加成本。

我认为,如果“代码”的数量变大,则开关盒结构生成的代码可能会变得相当大,在这种情况下,我认为stl::map更合适。

switch语句会更快,但如果这不是应用程序的性能瓶颈,您就不应该真正关心这一点

从长远来看,努力使代码更易于维护


您的示例太短,无法在这方面进行任何有意义的调用。

就我个人而言,我会使用映射,因为它的使用意味着数据查找-使用开关通常表示程序行为的不同。此外,使用映射比使用开关更容易修改数据映射

如果性能是一个真正的问题,那么分析是获得可用答案的唯一方法。如果分支预测失误发生得足够频繁,则切换可能不会更快

考虑这一点的另一种方法是,如果将代码和关联值组合到数据结构中没有更大意义,尤其是如果代码和值的范围是静态的:

struct Code { int code; int value; };

Code c = ...

std::cout << "Code " << c.code << ", value " << c.value << std::end;
结构代码{int-code;int-value;}; 代码c=。。。
std::cout这取决于代码是什么以及有多少代码。好的编译器有各种各样的技巧来优化switch语句,其中一些技巧不会用于直接的if/then语句。例如,对于案例0、1、2或案例3、6、9,大多数都足够聪明,可以进行简单的数学运算或使用查找/跳转表

当然,有些人没有,而且许多人很容易被不寻常或不规则的值集所挫败。另外,如果处理几种情况的代码看起来非常相似,则剪切和粘贴可能会导致维护问题。如果你有很多代码,但是它们可以被算法分成组,你可以考虑几个/嵌套的开关语句,例如,而不是:

switch (code) {
    case 0x0001: ...
    case 0x0002: ...
    ...
    case 0x8001: ...
    case 0x8002: ...
    ...
}
您可以使用:

if (code & 0x8000) {
    code &= ~0x8000;
    switch (code) {
        case 0x0001: ... // actually 0x8001
        case 0x0002: ... // actually 0x8002
        ...
    }
}
else {
    switch (code) {
        case 0x0001: ...
        case 0x0002: ...
        ...
    }
}
许多语言解释器以这种方式对操作码进行解码,因为单字节操作码可能会将附加信息打包到不同的位中,而转录所有可能的组合及其处理程序将是重复和脆弱的。另一方面,过多的位损坏可能会破坏编译器的任何优化,并且会适得其反

除非您确定这是一个真正的性能瓶颈,否则我将避免过早的优化:以您认为合理、健壮和快速实现的方式进行优化。如果您的应用程序运行太慢,请对其进行分析并进行相应的优化。

  • 将整数读入数组/向量
  • 对数组/向量进行排序
  • 在基础阵列上使用bsearch

    • 我自己也喜欢查找表,因为在我看来,异常长的switch语句似乎混淆了代码和数据之间的分离。我还认为表更适合将来的更改和维护


      当然是所有IMHO。

      如果可以使用tr1,那么可以使用无序映射来散列值(散列int也可以非常快),这将使大多数查找时间保持不变


      但是,除非您有分析数据表明这是程序中的瓶颈,否则请使用从设计角度来看最有意义的方法对其进行编码。

      我建议使用静态、常量、对表。这是查找表的另一种形式:

      struct Table_Entry
      {
          int code;
          int value;
      };
      
      static const Table_Entry  lookup_table[] =
      {
        {1, 10},
        {2, 15},
        {3, 13},
      };
      
      static const unsigned int NUM_TABLE_ENTRIES =
          sizeof(lookup_table) / sizeof(lookup_table[0]);
      
      这样做的一个好处是,表是在编译时生成的,而不是在运行时必须初始化的
      std::map
      。如果数量较大,可以使用
      std::lower_bound
      查找条目,前提是表格已排序

      另一个好处是这种技术是数据驱动的。数据可以在不更改搜索引擎的情况下更改。对代码或过程的更改可能需要进行严格的回归测试,但数据更改可能不需要;YMMV


      这与编译器可能生成的内容类似。

      通常我会建议使用映射或查找数组,或者甚至是混合查找怪物(当然,假设您正在优化速度或代码大小,而不是可读性),但值得注意的是,新版本的GCC足够聪明,可以取代像这样的开关/案例分配来查找表。虽然这对于完全稀疏的关键点来说并不是很好,但如果关键点是成束的,则可能是这样的: 0、1、2、3、4、5、11、12、13、14、15、193、194、195、196、197、198


      当然,如果你可能喜欢一些算术来做转换,甚至更好(通常)。在做这样的事情时,千万不要忽视移动、交换端点或更传统的数学

      无序映射可能会更适合这里,因为它使用哈希表,查找速度会比切换更快。

      无法使用枚举将整数转换为整数。它们不是连续的。这是问题的一部分。我正试图有效地将一个随机整数列表转换成另一个随机整数列表。@Rachel:编译时这两个随机列表都是已知的吗?如果是的,并且不连续性不大,那么查找表仍然是一种很好的方法。如果代码列表是