【问题标题】:What are the differences between returning values and side effect coding?返回值和副作用编码有什么区别?
【发布时间】:2014-02-07 14:47:39
【问题描述】:

我的问题是由 C++ 代码引起的,这不是我的,我目前正在努力理解。不过,我认为这个问题通常可以由 OO 开发人员来回答(因为我曾经在 Java 代码中看到过这种情况)。

通读代码,我注意到开发人员总是使用副作用(大多数函数具有“void return type”,除了 getter 和一些极少数情况)而不是直接返回结果。他有时会使用返回值,但仅用于控制流(错误代码...而不是异常)。

以下是他的原型的两个可能示例(以伪代码形式): 对于应该返回矩阵 M 中浮点值的最小值、最大值和平均值的函数:

void computeStatistics(float min, float max, float avg, Matrix M);

或

void computeStatistics(List myStat, Matrix M);

对于应该返回给定列表中的一些对象的函数,该列表验证特定条件和找到的对象数量:

int controlValue findObjects(List result, int nbObjectsFound, Object myCriteria, List givenList)

我不熟悉 C++,正如您可能在我的伪代码中看到的那样......但是使用 Matlab 可以从函数中返回您想要的所有内容,例如并排的 int 和 List (这可能对第二个例子有用)。我知道这在 C++ 中是不可能的,这可以解释第二个原型,但它没有解释第一个示例的选择,他可以这样做:

List myStat computeStat(Matrix M)

最后,这是我的问题: 可能激发这种选择的可能原因是什么?这是一种好的做法、惯例还是只是一种发展选择?一种方式比另一种方式有优势吗(返回值与副作用方式)?

【问题讨论】:

  • 一个函数接受一些它不会改变的参数并返回一个新对象,它在语义上比接受许多参数并改变其中一些参数的函数更清晰。但有时您确实需要修改现有对象。所以这一切都取决于你要解决的问题。
  • 请注意,在 C++ 中按值返回大对象通常不会受到惩罚,因此干净的解决方案也很有效。

标签: java c++


【解决方案1】:

在 C++ 方面:

IMO 使用返回值比通过引用传递值更清晰,并且在大多数情况下没有开销。 (请看RVO和Copy Elision)

但是,如果您确实为控制流使用了返回值,那么使用引用并不是一件坏事,而且对于大多数开发人员来说仍然很清楚。

所以我想我们可以说选择权在你。

还请记住,许多开发人员不知道您的 C++ 编译器在做什么黑魔法,因此使用返回值可能会冒犯他们。

【讨论】:

    【解决方案2】:

    过去,使用引用参数作为输出是一种常见的做法,因为在没有返回值优化和移动语义的情况下返回复杂对象非常慢。今天我相信在大多数情况下返回值是最好的选择。

    Want Speed? Pass by Value.

    【讨论】:

      【解决方案3】:

      如果列表有副本,我会认为写以下内容是不合适的。

      void computeStatistics(List myStat, Matrix M);
      

      您应该改为(如果该列表有副本)。

      List myStat computeStat(Matrix M)
      

      但是,如果您的对象上没有副本,则可以激发引用调用方法,那么您不需要在堆上分配它,而是可以在堆栈上分配它并向您的函数发送一个指针它。

      关于:

      void computeStatistics(float min, float max, float avg, Matrix M);
      

      我个人认为,最佳实践是一种方法,一种目的,所以我会这样做:

      float min computeMin(Matrix M);
      float max computeMax(Matrix M);
      float avg computeAvg(Matrix M);
      

      我可以看到在一个函数中完成所有这些的唯一原因是因为计算不是单独完成的(在单独的函数中完成更多工作)。

      但是,如果您需要在一种方法中具有多种返回类型,我会使用引用调用来实现。例如:

      void SomeMethod(input1, input2, &output1, &output2, &output3)
      

      【讨论】:

      • 您可能应该指定您正在谈论的语言。你一开始说的不适用于C++。该列表不必分配在任何堆上。
      • 前提是列表有副本。我写的内容更适用于没有副本的课程..
      • “使用第二种方法,您需要在堆上分配列表”,这根本不正确,除非您指定该类型不可复制或可移动,而您还没有这样做。容器和矩阵类型通常是可复制的,因此您的陈述非常具有误导性。
      • @juanchopanza 进行了编辑,希望它不会仍然误导!
      • 是的,这样的误导性较小。在我看来,传递引用的主要动机是当您真的想对现有对象进行修改时(即,将某些内容附加到列表、对容器进行排序等)
      猜你喜欢
      • 2011-12-24
      • 1970-01-01
      • 2012-03-22
      • 2016-06-28
      • 1970-01-01
      • 1970-01-01
      • 2015-02-27
      • 2015-03-31
      • 2020-11-19
      相关资源
      最近更新 更多