多个返回语句或"goto end;"

multiple return statements or a "goto end;"

本文关键字:goto end 返回 语句      更新时间:2023-10-16

什么是更好的风格/可读性?

我有一个循环,可以读取输入并根据输入做不同的事情,当发生错误时,我需要一个简单的return;。例:

while( get_input() )
{
    if( input == "somethingcool" )
    {
        if( !process_somethingcool() )
            return; // <-- a couple of these
    }
    //...
    else // bad input, error handling is fancier than this, but irrelevant to the question
        return;
}
return;

那么我是否应该用goto end;替换单个return;,并在上面示例中的最后一个返回end:正上方放置一个标签?我不需要"使用 RAII",因为if块中没有分配任何内容。这两种方式在这个词的所有意义上都是相同的,除了风格/可读性/性能?

我认为性能是相同的,但可以肯定的是:是吗?

对于 C,goto 是合理的(它在 Linux 内核中广泛使用),因为您可以通过单点返回来增强可读性。

对于C++,由于您有可能引发异常,因此隐式有多个返回点,因此应始终使用具有多个返回的 RAII 模型。

你想发动一场宗教战争吗?

说真的,偶尔有些地方goto是最好的选择。 二十年来,我见过大约 3 或 4 个。 多次返回不一定是邪恶的,但是如果你必须重复大量的清理代码,那么它们就会变得非常混乱。

通常,您可以重构代码以使此类选择变得不必要。 如果不看到您的代码,很难提出具体建议,但可能是这样的:

void f()
{
   bool bDone=false;
   while (!bDone && get_input())
   {
      if (input == "cool")
      {
         process_cool();
         bDone = true;
      }
      else if (input == "unfinished")
      {
         process_something();
      }
      else
      {
          // error
          bDone = true;
      }
   }
}

重构的一大帮助是确保循环中没有几十行。 如果你有大量的工作要做,把它分解成函数,并从 while 循环中调用少量函数。

请记住,单个函数只能做一件事。

强烈建议的另一种方法是使用异常来处理错误条件,但如果刚刚完成处理,则使用异常是一种不好的风格,因此这可能无法完全解决您的问题。

如果您仍然感到困惑,请考虑发布更现实的代码块,我们也许能够建议如何最好地处理事情。

祝你好运!

就个人而言,我喜欢将任何块的内容保持在尽可能小的范围内。 一行,调用另一个函数是理想的(在鲍勃马丁的清洁代码之后)。

我会选择您尚未提出的选项:

while(get_input()) {
    if(!process_input(input)) {
         break;
    }
}

其中process_input将选择适当的process_...函数,返回返回的任何内容,如果输入错误,则返回 false。

想回来就回来。C++语言可以处理这个问题,这是编写代码的最直观方式。

在某些语言中,清理必须在使用站点完成,因此将"清理"阶段集中在您"转到"的单个块中可能是个好主意。在C中,这个成语很常见。

在C++中,当资源超出范围时,资源会在其析构函数中自行清理。因此,在使用现场,什么都不需要做,最简单、最干净、最不容易出错的解决方案就是拥有多个 return 语句。

多个返回语句实际上是很好的风格,并且几乎总是产生比尝试具有单个返回点更干净的代码。 gotos 在C++中非常有用,因为(除了它们的其他问题)它们无法跳过初始化,这可能会迫使您从它们的使用点初始化东西,这也是不好的风格。

样式和可读性会导致我使用异常来处理错误条件。 调用方如何知道您的方法是否已正确调用?

也就是说:goto不应该用于此; return是更好的选择。 如果你需要在例程结束时做任何事情,不管它为什么退出 - 那么你应该抛出异常并使用catch块。

return通常比goto更受欢迎;通常,标签的批评者无法提出任何实际的论据来支持他们的教条。1

但是,您的情况更明确:为什么要选择goto?这两种解决方案都是等效的,只是goto要求您在函数结束之前编写end:,无缘无故地占用空间并且看起来很丑。

因此,在提供的两个选项中,我推荐return s。也就是说,我还建议认真考虑在回答这个问题时给出的其他建议(例外,并限制条件的"嵌套性")。


1 或者,如果他们这样做,通常是类似于"goto让你的物体悬而未决",这是不正确的(根据 C++0x FDIS 中的 6.6/2)。 goto不会破坏 RAII 模型

goto End;之所以不错,部分是因为可读性,还因为如果您稍后意识到在关闭函数之前需要发生一些事情,例如释放内存或释放资源,您可以集中该过程而不是复制粘贴它。

实际上,不建议使用goto。它会在代码的可读性方面产生问题。最好使用 break; 语句,您希望离开循环并在代码末尾返回。这是标准程序。

while( get_input() )
{
    if( input == "somethingcool" )
    {
        if( !process_somethingcool() )
            break; // <-- a couple of these
    }
    //...
    else // bad input, error handling is fancier than this, but irrelevant to the question
        break; //although its better if you handle it somehow here.
}
return;

为什么不使用"保护子句"(通过反转逻辑)而不是嵌套的 ifs 来构建代码。然后,使用goto的整个问题变得毫无意义。

while( get_input() )
{
    if( input != "somethingcool" )
    {
        return; //handle error
    }
    if( !process_somethingcool() )
        return; //handle error
    }
}
return; //success

两者都不是。 您应该将代码重构为可读的内容并且可维护。 我见过的每一个案例(而且我见过很多)程序员需要goto的地方,以及大多数情况下他需要多次返回的地方(以及所有返回已嵌套在循环中)最好通过以下方式解决将代码重构为单独的、更简单的函数,每个函数在函数结束时有一个单一的返回。 (多个对于非常简单的函数,有时返回是合理的;例如如果函数中唯一的语句是一个switch,每个案例都以return结尾。

恕我直言,使用多个返回,不使用goto。那些使用goto的人,也会放火烧狗。