高效的开关声明

时间:2009-12-04 14:15:23

标签: c++ performance switch-statement

在以下两个版本的switch case中,我想知道哪个版本是高效的。

1:

string* convertToString(int i)
{
    switch(i)
    {
    case 1:
        return new string("one");
    case 2:
        return new string("two");
    case 3:
        return new string("three");
        .
        .
    default:
        return new string("error");
    }
}

2:

string* convertToString(int i)
{
    string *intAsString;
    switch(i)
    {
    case 1:
        intAsString = new string("one");
        break;
    case 2:
        intAsString = new string("two");
        break;
    case 3:
        intAsString = new string("three");
        break;
        .
        .
    default:
        intAsString = new string("error");
        break;
    }
return intAsString;
}

1:有多个return语句会导致编译器生成额外的代码吗?

14 个答案:

答案 0 :(得分:29)

这是一个过早的优化问题。

前一种形式更清晰,源线更少,当然是选择它的一个令人信服的理由(在我看来)。

您应该(照常)对您的程序进行概要分析,以确定此功能是否在“热门列表”中进行优化。这将告诉您使用break是否存在性能损失。

正如评论中所指出的,这个代码的主要性能元凶很可能是动态分配的字符串。通常,在实现这种“整数到字符串”映射函数时,应该返回字符串常量。

答案 1 :(得分:10)

两者都是。

你应该关注的是你在这里使用指针。有必要吗?谁会删除这些字符串?是否有更简单的替代方案?

答案 2 :(得分:6)

编译代码应该没有区别。 但是:

  1. 您可能会发现按值返回字符串会更有效率。
  2. 如果有很多字符串,请考虑使用它们预填充向量(或声明静态数组)并使用i作为索引。

答案 3 :(得分:3)

除非使用特定的编译器版本,特定的设置集和特定的代码库进行编译,否则您永远不会知道优化将如何影响所生成的代码。

C ++优化编译器可能会决定颠倒您的源代码,以获得仅适用于编译器体系结构的特定优化,而您无需了解它。强大的优化编译器可以例如发现只需要10个案例中的2个,并将优化掉整个switch-case语句。

所以我对你问题的回答是:Mu

答案 4 :(得分:3)

switch语句基本上是一系列if语句,作为生成的机器指令。一个简单的优化策略是将最常见的case放在switch语句中。

我也推荐与Sebastian相同的解决方案,但没有断言。

static const char *numberAsString[] = {
    "Zero",
    "One",
    "Two",
    "Three",
    "Four",
    "Five",
    "Six",
};

const char *ConvertToString(int num) {
  if (num < 1 || num >= (sizeof(numberAsString)/sizeof(char*))) 
    return "error";
  return numberAsString[num];
}

答案 5 :(得分:2)

如果你打开优化,两个函数很可能会生成相同的代码。

答案 6 :(得分:1)

编译器很可能会将两个版本优化为相同的代码。

答案 7 :(得分:1)

他们几乎肯定都会被编译成一个相同的,高效的分支表。使用你觉得更清楚的那个。

答案 8 :(得分:1)

我会建议一些形式:

void CScope::ToStr( int i, std::string& strOutput )
{
   switch( i )
   {
   case 1:
        strOutput = "Some text involving the number 1";

   ... etc etc
}

通过返回指向堆上创建的字符串的指针,可能会导致内存泄漏。特别是关于你的问题,我建议最少数量的返回路径比过早优化更合适。

答案 9 :(得分:1)

考虑将字符串保持为静态常量:

static char const g_aaczNUMBER[][] = 
    {
        {"Zero"}, { "One" }, ...
    };

static char const g_aczERROR[] = { "Error" };

char* convertIntToString(int i) const { 
    return i<0 || 9<i ? g_aczERROR : g_aaczNUMBER[i]; 
}

答案 10 :(得分:1)

通过在交换机中尽可能少地工作来优化[*] switch语句(因为不确定编译器是否会共享复制)。如果你坚持用指针返回一个字符串,并使用switch语句,我会写这个:

string *convertToString(int i) {
    const char *str;
    switch(i) {
        case 1 : str = "one"; break;
        // etc
        default : str = "error"; break;
    }
    return new string(str);
}

但是当然对于这个例子我可能只是使用查找表:

const char *values[] = {"error", "one", ... };

string convertToString(unsigned int i) {
    if (i >= sizeof(values)/sizeof(*values)) i = 0;
    return values[i];
}

那就是说,我刚回答了一个关于静态初始化顺序惨败的问题,所以你通常不需要经验法则来求全局变量。你所做的事情必须依赖于函数的上下文。

[*]我指的是在编写可移植代码时或在第一版中执行的经验法则优化,希望创建清晰易读且不需要太多的代码真正的优化。真正的优化涉及实际测量。

答案 11 :(得分:0)

这里的效率没有任何差别。当然没有一件事无关紧要。使用选项#2的唯一好处是,如果您需要对适用于所有情况的字符串进行一些后处理。

答案 12 :(得分:0)

不应存在任何可衡量的差异,返回语句不应生成任何机制。他们应该在调用点的堆栈上放置一个指向字符串对象(在堆上分配)的指针。

答案 13 :(得分:0)

有趣的部分是你担心休息的效率然后返回,但每次都会创建一个新的字符串。

答案取决于编译器,但无论如何都应该无关紧要。如果你一直打电话,请避免使用新字符串。

交换机通常可以进行优化,以便它执行跳转而不是一堆if if,但是如果你查看汇编源代码,那么优化器的工作量通常会让你不知所措。