C ++ Long switch语句还是用map查找?

时间:2010-03-17 20:42:46

标签: c++ dictionary switch-statement std stdmap

在我的C ++应用程序中,我有一些值作为代码来表示其他值。要翻译代码,我一直在讨论使用switch语句或stl映射。开关看起来像这样:

int code;
int value;
switch(code)
{
case 1:
    value = 10;
    break;
case 2:
    value = 15;
    break;
}

地图将是stl::map<int, int>,翻译将是一个简单的查找,其中代码用作键值。

哪一个更好/更有效/更清洁/接受?为什么呢?

12 个答案:

答案 0 :(得分:8)

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

如果性能是一个真正的问题,分析是获得可用答案的唯一方法。如果分支误预测经常发生,开关可能不会更快。

考虑这个问题的另一种方法是将代码和相关值组合到数据结构中是否更有意义,特别是如果代码和值的范围是静态的:

struct Code { int code; int value; };

Code c = ...

std::cout << "Code " << c.code << ", value " << c.value << std::end;

答案 1 :(得分:7)

如果你的代码足够连续并且它们的范围允许你使用老式的直接数组会更好,比如

int lookup[] = {-1, 10, 15, -1 222};

然后可以像

那样简单地重写switch语句

value = lookup [code];

所有其他选项在某种程度上都会带来额外成本。

答案 2 :(得分:5)

这取决于代码是什么以及有多少代码。好的编译器有各种各样的技巧来优化switch语句,其中一些不会直接使用if / then语句。大多数都足够明亮,可以进行简单的数学运算,或者使用例如0,1,2或3,6,9的查找/跳转表。

当然有些人没有,很多很容易因不寻常或不规则的价值观而挫败。此外,如果处理多个案例的代码看起来非常相似,剪切和粘贴可能会导致维护问题。如果您有许多代码,但它们可以通过算法划分为组,您可以考虑使用多个/嵌套的switch语句,而不是:

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: ...
        ...
    }
}

许多语言解释器以这种方式解码操作码,因为单字节操作码可能具有打包到各种位中的附加信息,并且转录所有可能的组合及其处理程序将是重复且易碎的。另一方面,过多的位错误可能会破坏编译器的任何优化并且会适得其反。

除非你确定这是一个真正的性能瓶颈,否则我会避免过早优化:以合理的强大和快速实施的方式为您提供服务。如果您的应用程序运行速度太慢,请对其进行分析并进行相应的优化。

答案 3 :(得分:2)

switch语句会更快但如果这不是你的应用程序性能瓶颈,你就不应该真的关心它。

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

你的样本太短,无法在这方面做出任何有意义的电话。

答案 4 :(得分:2)

我不喜欢自己查找表,因为在我看来,异常冗长的switch语句会混淆代码和数据之间的分离。我还认为表格可以更好地适应未来的变化和维护。

当然,所有恕我直言。

答案 5 :(得分:2)

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

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。

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

答案 6 :(得分:1)

如果你可以使用tr1,你可以使用unordered_map来散列值(哈希int也可以非常快),这应该使大多数查找保持不变。

但是,除非您对数据进行性能分析以表明这是程序中的瓶颈,否则请从设计角度对其进行最有效的方法进行编码。

答案 7 :(得分:0)

如果你正在做的就是分配一个值,我说地图。我的理由是它可以在不改变代码的情况下进行扩展,而你的switch语句不是。

顺便问一下,enum怎么样?

答案 8 :(得分:0)

我认为如果“代码”的数量变大,生成的switch-case结构代码会变得非常大,在这种情况下我认为stl :: map更合适。

答案 9 :(得分:0)

通常我建议使用地图或查找数组,或者甚至是一些混合查找怪物(假设您正在优化速度或代码大小而不是可读性,当然),但值得注意的是新版本的GCC足够聪明,可以将这样的交换机/案例分配替换为查找表。虽然这对于完全稀疏的密钥来说并不是那么好,但如果你的密钥是块状的,那就像: 0,1,2,3,4,5,11,12,13,14,15,193,194,195,196,197,198 ......

当然,如果你可能喜欢一些算术来进行转换,那就更好了(通常)。在做这样的事情时,永远不要忽视转移,交换字节序或更传统的数学。

答案 10 :(得分:0)

unordered_map可能会更适合此处,因为它正在使用哈希表,并且查找会比使用switch更快。

答案 11 :(得分:-1)

  • 将整数读入数组/向量
  • 对数组/向量进行排序
  • 在底层数组上使用bsearch