8

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

(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) 的性能应该更差,不是吗?

4

5 回答 5

3

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

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

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

于 2012-09-19T12:30:56.330 回答
3

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

在他的“重构:改进现有代码的设计”一书中,Martin Fowler 将这种临时性的消除列为一种可能有益的重构(Inline temp)。

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

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

最后,选择引入或移除这种临时措施是应根据具体情况做出的决定。如果它使代码更具可读性,请留下它。如果只是噪音,请将其删除。在您的特定示例中,我会说临时不会增加太多,但是如果不知道实际代码中使用的真实名称,这很难说,您可能会有不同的感觉。

于 2012-09-19T12:31:45.183 回答
2

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

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

在此示例中,性能不应成为瓶颈。总之,最小化错误的机会并强调相同的值比潜在的小性能提升更重要。

于 2018-06-29T23:01:46.780 回答
1

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

同意。

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

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

此外,TestFunction可能会有副作用,但在这种情况下,代码或注释应该以某种方式说明这一点。

于 2012-09-19T12:17:13.120 回答
1

两者不等价。举个例子:

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

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

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

于 2012-09-19T12:17:44.907 回答