单入/单出退出规则

Nay*_*iya 7 c++ return-value

我在某处读过一条规则:

遵循单入口/单出口规则.永远不要在同一个函数中写多个return语句.

这个陈述是真的吗?如果是这样,请您详细说明我们为什么要遵守这条规则?

Dav*_*men 18

这是真的?

这就是规则所说的,在其使用和执行的地方.这是一个好规则吗?我反对它采用牙齿和钉子.我认为这是一个愚蠢的规则.比愚蠢更糟糕:这对C++来说是一个有害的规则.

我同意规则的第一部分"单一条目".Fortran entry语句引发的问题比解决的问题多得多.规则的第一部分与C或C++无关,原因很简单,两种语言都没有提供多入口点机制."单一条目"是C和C++中的无操作.

那么"单一退出"呢?提前退货并不一定会导致问题.在返回之前未能处理分配的资源是导致问题的原因.正确的规则是"清理你的烂摊子",或者不留下悬空资源.单一退出并不能解决这个问题,因为它没有说清理你的烂摊子的事情.

在C中,单入口/单出口规则通常与允许(甚至鼓励)goto用于错误处理的方式齐头并进.我可以goto在Linux内核代码中看到用于错误处理的地方.但不是在C++中.这就是为什么我写道单条目/单一退出在C++中是有害的.此规则不鼓励使用RAII和异常安全编程,并鼓励使用goto.

  • 你的答案和SingerOfTheFall应该合并在一起来解释为什么单一出口是非常荒谬的. (2认同)

Sin*_*all 13

不,这不是一个规则,有时甚至很难/不可能实现.具有一个条目和一个出口点的代码更容易理解和调试.比较一下:

int foo()
{
    if(something)
        return 0;
    //100 lines of code
    if(something)
        return 11;
    //100 lines of code
    if(something)
        return -1;
    //100 lines of code
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

还有这个:

int foo()
{
    int errorCode = 0;
    if(something)
        errorCode = 1;
    //100 lines of code
    if(something)
        errorCode = 11;
    //100 lines of code
    if(something)
        errorCode = -1;
    //100 lines of code
    return errorCode;
}
Run Code Online (Sandbox Code Playgroud)

现在我们只有一个退出点,并且(考虑到变量名称),它更容易理解函数的作用.你也可以在最后一个返回点放置一个断点,并知道这是函数结束的点,你肯定会遇到它.

  • 使用`errorCode`的示例可能不是最好的,因为您可能希望在错误发生的地方实际停止执行,但我希望您能够理解. (4认同)
  • 实际上,你的第二个例子可能会被窃听,如果它应该与第一个一样,因为"return"停止执行该函数,而你继续使用下面的代码.在这种情况下,我通常检查在开头设置的特殊"未设置"值的返回值. (3认同)
  • 此代码或已接受答案中的代码的最大问题是一个 310 行长的函数。即使按照大多数 C 标准,这也很长。 (2认同)

Gor*_*pik 5

此规则可能适用于C,但由于例外,它可以在C++中被认为是过时的.只要您的函数抛出异常或调用可以抛出的函数,您就会有一个额外的退出点:

int f()
{
  //...
  g(); // g() may throw: you have an exit point here
  //...
  throw exc; // another possible exit point
  //...
  return returnValue; // Nice try, but you have additional exit points
}
Run Code Online (Sandbox Code Playgroud)

除了在其他答案中提出的观点之外:此规则旨在使代码更易于遵循,但很容易找到不适用的示例.好多了:

if (condition)
  return a;
if (condition2)
  return b;
if (condition3)
  return c;

// Insert all your code for the general case
Run Code Online (Sandbox Code Playgroud)

比:

int returnValue;    
if (!condition) {
  if (!condition2) {
    if (!condition3) {
      // Insert your code here
    }
    else {
      returnValue = c;
    }
    returnValue = b;  // Where am I now?
  }
  returnValue = a;
}
return returnValue;
Run Code Online (Sandbox Code Playgroud)

然后,当您在以下情况中确定返回值时,您也会遇到这种情况switch:

switch (a)
{
  case 1: return 10;
  case 2: return 20;
  case 3: return 40;
  default: return 50;
}
Run Code Online (Sandbox Code Playgroud)

而不是:

int returnValue;
switch (a)
{
  case 1: returnValue = 10; break;
  case 2: returnValue = 20; break;
  case 3: returnValue = 40; break;
  default: returnValue = 50; break;
}
return returnValue; // Where is the clarity gained?
Run Code Online (Sandbox Code Playgroud)

  • 一些编码标准具有"无例外"规则.那些同样具有"单一入境点/单点退出"规则的人根据这一特别糟糕的规则证明了"无例外"规则. (2认同)

mar*_*ark -2

我个人并不反对提前退出,但我会提出 SingerOfTheFall 的第三种替代方案供考虑。

优点:

  • 正常的代码流(即非错误)干净地流过代码顶部
  • 不可能在无意中执行一段代码时使一个“某事”失败并传递另一个“某事”
  • 您可以强制代码块的范围;包括在退出范围时清理子代码块中使用的内容

坏处:

  • 缩进可能会增加(尽管这可以通过分解子功能来缓解)
  • 如果编辑器中没有大括号匹配,则很难将错误与失败条件匹配
int foo()
{
    int 错误代码 = 0;
    如果(!某事){
        //100行代码
        如果(!某事){
            //100行代码
            如果(!某事){
                //100行代码
            }
            别的 {
                错误代码=-1;
            }
        }
        别的 {
            错误代码=11;
       }
    }
    别的 {
        错误代码=1;
    }
    返回错误代码;
}