【问题标题】:Function calls vs. local variables函数调用与局部变量
【发布时间】:2012-09-19 12:07:12
【问题描述】:

我经常看到其他函数被多次调用的函数,而不是一次存储函数的结果。

(1)

void ExampleFunction()
{ 
    if (TestFunction() > x || TestFunction() < y || TestFunction() == z)
    {
        a = TestFunction();
        return; 
    }
    b = TestFunction();
}

我会这样写,(2)

void ExampleFunction()
{
    int test = TestFunction();
    if (test > x || test < y || test == z)
    {
        a = test;
        return;
    }
    b = test;
}

我认为版本 2 更易于阅读和调试。 但我想知道为什么人们会像第一名那样做? 有什么我看不到的吗?性能问题? 当我查看它时,在最坏的情况下,我看到数字 (1) 中的 4 个函数调用而不是数字 (2) 中的 1 个函数调用,所以数字 (1) 的性能应该更差,不是吗?

【问题讨论】:

  • 问问写代码的人!
  • 编译器可能会将代码优化为类似于您的替代方案两个,因此性能不是问题。我自己,我发现第一个替代方案实际上更具可读性。
  • 可能会有副作用,这意味着每次都必须调用该函数。因此,您的问题的答案是:视情况而定。
  • @JoachimPileborg 我不相信编译器会做这种优化,除非TestFunction 无条件地返回一个编译时常量或类似的。如前所述,可能会有副作用,或者每次调用时返回的值都会发生变化。在这些情况下,优化会改变程序的行为。
  • @Default:事实上,这两条代码通常做不同的事情。因此,如果没有其他信息,这个问题并不真正合法。

标签: c++ function variables call local


【解决方案1】:

如果我想强调在整个代码中使用相同的值,或者我想强调该值的类型是int,我会使用 (2)。强调真实但不明显的东西可以帮助读者快速理解代码。

如果我不想强调其中任何一个,尤其是如果它们不是真的,或者由于副作用而调用 TestFunction() 的次数很重要,我会使用 (1) .

很明显,如果您强调某些当前为真,但在未来TestFunction() 发生变化并变为假,那么您就有了错误。所以我也想要么自己控制TestFunction(),要么对作者未来兼容性的计划有一些信心。通常这种信心很容易:如果 TestFunction() 返回 CPU 的数量,那么您很乐意拍摄该值的快照,并且您也很乐意将其存储在 int 中,无论它实际返回什么类型.你必须对未来的兼容性有最小的信心才能使用一个函数,例如。相信它将来不会返回键盘的数量。但是不同的人有时会有不同的想法,什么是“重大变化”,尤其是当界面没有准确记录时。因此,对TestFunction() 的重复调用有时可能是一种防御性编程。

【讨论】:

  • 如果要去掉“强调int”,可以改用auto
  • @larsmans: 是的,如果TestFunction 返回一个引用,甚至是auto&amp;
  • 强调它的一个可能原因是,在阅读比较时,您需要知道所涉及的类型是有符号、无符号还是各有一个。所以这是强调类型而不是将其留给TestFunction 的原因。即使假设读者在 IDE 中并且可以轻松地查找类型,他们也可能会受益于知道 知道您期望什么类型。 auto 很有用,给类型命名也很有用,各取所需。
【解决方案2】:

当使用临时变量来存储像这样一个非常简单的表达式的结果时,可以说临时变量引入了应该消除的不必要的噪音。

在他的书 "Refactoring: Improving the Design of Existing Code" 中,Martin Fowler 将这种临时性的消除列为可能有益的重构 (Inline temp)。

这是否是一个好主意取决于很多方面:

  • 临时表达式是否提供了比原始表达式更多的信息,例如通过有意义的名称?
  • 性能重要吗?正如您所指出的,没有临时的第二个版本可能更有效(大多数编译器应该能够优化此类代码,以便该函数只调用一次,假设它没有副作用)。
  • 临时在函数后面修改吗? (如果不是,应该是const

归根结底,是否引入或删除此类临时文件是应根据具体情况做出的决定。如果它使代码更具可读性,请保留它。如果只是噪音,请将其删除。在您的特定示例中,我会说临时并没有增加太多,但是如果不知道您的实际代码中使用的真实名称,这很难说,您可能会有不同的感觉。

【讨论】:

    【解决方案3】:

    第二种选择显然更胜一筹。

    您要强调确保 if 语句中的值是相同值的三倍。

    性能不应成为本示例中的瓶颈。总之,尽量减少出错的机会并强调相同的值比潜在的小幅性能提升更重要。

    【讨论】:

    • 我同意这里,特别是对于 OP 给出的示例。其他答案在许多情况下肯定是信息丰富且适用的,但在 OP 的示例中,一个值(由函数返回)似乎很明显满足三个条件。如果函数每次调用都没有返回相同的值,那么在情况 (1) 中这段代码会非常混乱。对此答案 +1。
    【解决方案4】:

    两者不等价。举个例子:

    int TestFunction()
    {
       static int x;
       return x++;
    }
    

    但在一个理智的世界中,情况并非如此,我同意第二个版本更好。 :)

    如果函数由于某种原因不能被内联,第二个甚至会更高效。

    【讨论】:

      【解决方案5】:

      我认为版本 2 更易于阅读和调试。

      同意。

      所以数字 (1) 的性能应该更差,不是吗?

      不一定。如果TestFunction 足够小,那么编译器可能会决定优化多个调用。在其他情况下,性能是否重要取决于调用ExampleFunction 的频率。如果不经常,则针对可维护性进行优化。

      另外,TestFunction 可能有副作用,但在这种情况下,代码或 cmets 应该以某种方式明确这一点。

      【讨论】:

      • 好的,谢谢,这对我来说很有意义。无论如何,我想到了没有副作用的功能。所以我想我会选择数字(2)。由于阅读和调试更好,性能可能更好(如果不能内联)。
      • 据我所知,编译器现在包含Link-time optimizations,这意味着即使TestFunction 定义在与ExampleFunction 不同的翻译单元中,它们也应该能够优化此类代码。跨度>
      • @LucTouraille:我认为这几乎仅限于英特尔 C。谢谢!
      猜你喜欢
      • 2015-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-11
      • 1970-01-01
      • 2022-08-02
      • 1970-01-01
      相关资源
      最近更新 更多