是否有充分的理由说明在函数中只有一个return语句是一个更好的实践?

或者在逻辑上正确的情况下,可以从函数中返回,这意味着函数中可能有许多返回语句?


当前回答

我倾向于单一退出,除非事情真的变得复杂。我发现在某些情况下,多个存在点可以掩盖其他更重要的设计问题:

public void DoStuff(Foo foo)
{
    if (foo == null) return;
}

在看到这段代码时,我马上会问:

'foo'是否为空? 如果是这样,有多少客户端'DoStuff'曾经调用一个空'foo'函数?

根据这些问题的答案,可能是这样

这种检查毫无意义,因为它从来都不是真的。它应该是一个断言) 这种检查很少是正确的,所以最好改变那些特定的调用函数,因为它们可能应该采取一些其他的操作。

在上述两种情况下,代码可能都可以用断言重做,以确保'foo'永远不为空,并更改相关的调用者。

还有另外两个原因(我认为是针对c++代码的),多重存在实际上会产生负面影响。它们是代码大小和编译器优化。

在函数出口作用域中的非pod c++对象将调用其析构函数。如果有几个return语句,那么作用域中可能有不同的对象,因此要调用的析构函数列表也会不同。因此,编译器需要为每个return语句生成代码:

void foo (int i, int j) {
  A a;
  if (i > 0) {
     B b;
     return ;   // Call dtor for 'b' followed by 'a'
  }
  if (i == j) {
     C c;
     B b;
     return ;   // Call dtor for 'b', 'c' and then 'a'
  }
  return 'a'    // Call dtor for 'a'
}

如果代码大小是一个问题,那么这可能是值得避免的。

另一个问题涉及到“命名返回值优化”(又名复制省略,ISO c++ '03 12.8/15)。c++允许实现在可以的情况下跳过调用复制构造函数:

A foo () {
  A a1;
  // do something
  return a1;
}

void bar () {
  A a2 ( foo() );
}

就像代码一样,对象'a1'是在'foo'中构造的,然后它的复制构造将被调用来构造'a2'。然而,复制省略允许编译器在堆栈上与'a2'相同的位置构造'a1'。因此,当函数返回时,不需要“复制”对象。

多个出口点会使编译器的检测工作复杂化,至少对于相对较新的vc++版本,优化不会发生在函数体有多个返回的地方。有关详细信息,请参阅Visual c++ 2005中的命名返回值优化。

其他回答

正如Kent Beck在《实现模式》中讨论保护子句时指出的那样,使一个例程具有单一的入口和出口点……

是为了防止可能的混淆 当你在许多地方跳来跳去 同一例程中的位置。这让 当应用到FORTRAN或 汇编语言程序 有大量的全球数据,甚至 理解哪些语句是 执行是一项艰苦的工作……使用小方法和大部分本地数据,它是不必要的保守。”

我发现用保护子句编写的函数要比一长串嵌套的if then else语句容易理解得多。

I've seen it in coding standards for C++ that were a hang-over from C, as if you don't have RAII or other automatic memory management then you have to clean up for each return, which either means cut-and-paste of the clean-up or a goto (logically the same as 'finally' in managed languages), both of which are considered bad form. If your practices are to use smart pointers and collections in C++ or another automatic memory system, then there isn't a strong reason for it, and it become all about readability, and more of a judgement call.

如果你最终得到了很多返回值,那么你的代码可能有问题。否则,我同意有时能够从子例程的多个位置返回是很好的,特别是当它使代码更干净的时候。

Perl 6:坏例子

sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}

这样写会更好吗

Perl 6:好例子

@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}

注意,这只是一个简单的例子

Structured programming says you should only ever have one return statement per function. This is to limit the complexity. Many people such as Martin Fowler argue that it is simpler to write functions with multiple return statements. He presents this argument in the classic refactoring book he wrote. This works well if you follow his other advice and write small functions. I agree with this point of view and only strict structured programming purists adhere to single return statements per function.

函数中return语句越多,该方法的复杂性就越高。如果您发现自己想知道是否有太多的return语句,那么您可能需要问问自己,该函数中是否有太多的代码行。

但是,不是,一个/多个返回语句并没有错。在某些语言中,这是一种比其他语言(C)更好的实践(c++)。