多个返回语句或"goto end;"
multiple return statements or a "goto end;"
什么是更好的风格/可读性?
我有一个循环,可以读取输入并根据输入做不同的事情,当发生错误时,我需要一个简单的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的人,也会放火烧狗。
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 在 ubuntu3 上C++ goto 定义有什么解决方案吗16.04?
- 如何正确指定 goto 语句的标签?
- 反转依赖于 end() 的迭代器
- std::multimap<std::chrono::milliseconds, T>::rbegin 在 MSVS-13 中指向 end()?
- 在C++中使用 Catch 测试框架编译错误"error: expected ';' at end of declaration list"
- ANTLR GOTO statement
- 为什么我的功能在使用 goto 时会给我带来"expected primary-expression before '}' token"?
- remove(str.begin(), str.end(), );无法正常工作(我正在使用视觉工作室 2012)
- 在 ifcondition al中 find() C++ STL 中的 == a.end() 有什么用?
- 野牛/yacc 解析器在不被空格分隔时跳过 grammer - "unexpected $end"
- "错误 C0000:语法错误,令牌"<EOF>"处出现意外$end,并且不确定
- 了解向量中的 .begin 和 .end
- 如何实现链表的 end()?
- 使用 map.end() 访问 map 的最后一个元素
- C++ const char with .begin() and .end()
- 接收"Error compiling: 0:1(1): error: syntax error, unexpected $end" C++、GLSL、着色器文件
- 如何在复杂的算法中处理goto函数?
- 多个返回语句或"goto end;"