单入/单出退出规则

时间:2012-10-05 11:33:02

标签: c++ return-value

我在某处读过一条规则:

  

遵循单项/单退出规则。切勿在同一函数中写入多个return语句。

这句话是真的吗?如果是这样,您能否详细说明我们为什么要遵循这条规则呢?

6 个答案:

答案 0 :(得分:16)

  

这是真的吗?

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

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

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

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

答案 1 :(得分: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;
}

和此:

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;
}

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

答案 2 :(得分: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
}

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

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

// Insert all your code for the general case

比:

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;

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

switch (a)
{
  case 1: return 10;
  case 2: return 20;
  case 3: return 40;
  default: return 50;
}

而不是:

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?

答案 3 :(得分:2)

此外,多个出口可能是一些性能问题:当处理器运行当前命令时,在同一时钟滴答时它会处理几个下一个命令并对它们执行一些操作。因此,如果您的代码有多个退出,如下所示:

if (condition)
  return a;
DoSomething();
if (condition2)
  return b;

并且第一个条件为真,提取DoSomething()命令将毫无用处。 实际上,通过分支预测它仍然可以很好,但无论如何最好记住这一点。

答案 4 :(得分:0)

如果要实现此目的,可以将返回值存储在变量中,但事实并非如此。您可以在同一个函数中有多个返回而没有问题。

答案 5 :(得分:0)

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

优点:

  • 正常的代码流(即非错误)流畅地流过顶部 代码
  • 没有机会失败一个“东西”并传递另一个 “某事”无意中执行了一段代码
  • 您可以在代码块上强制执行范围;包括清除退出代码子块中使用的东西的范围

缺点:

  • 缩进可以加起来(虽然这可以通过打破来减轻 进入子功能)
  • 在编辑器中没有大括号匹配,可能很难将错误与失败的条件匹配
int foo()
{
    int errorCode = 0;
    if(!something) {
        //100 lines of code
        if(!something) {
            //100 lines of code
            if(!something) {
                //100 lines of code
            }
            else {
                errorCode = -1;
            }
        }
        else {
            errorCode = 11;
       }
    }
    else {
        errorCode = 1;
    }
    return errorCode;
}