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:编译时这两个随机列表都是已知的吗?如果是的,并且不连续性不大,那么查找表仍然是一种很好的方法。如果代码列表是