单一进入/单一退出规则
Single-entry/single-exit rule
我在某处读到了一条规则:
遵循单次进入/单次退出规则。切勿在同一函数中写入多个返回语句。
这句话是真的吗?如果是这样的话,你能详细说明我们为什么要遵守这条规则吗?
这是真的吗?
这就是规则所说的,在使用和执行规则的地方。这是一条好规则吗?我竭尽全力反对它被收养。我认为这是一条愚蠢的规则。比愚蠢更糟糕:这对C++来说是一条有害的规则。
我同意规则的第一部分,"单一条目"。Fortranentry
语句引起的问题比它解决的问题多得多。规则的第一部分与C或C++无关,原因很简单,因为这两种语言都不提供多入口点机制。在C和C++中,"单条目"是一个非操作。
那么"单一出口"呢?提前返回不一定会造成问题。未能在返回之前处理分配的资源是造成问题的原因。正确的规则是"收拾残局",或者不要留下悬而未决的资源。单一出口并不能解决这个问题,因为它根本没有说明要清理你的烂摊子。
在C中,单入单出规则通常与允许(甚至鼓励)使用goto
进行错误处理齐头并进。我可以看到goto
在Linux内核代码中用于错误处理的位置。但在C++中没有。这就是为什么我写的单入口/单出口在C++中是有害的。该规则不鼓励使用RAII和异常安全编程,并鼓励使用goto
。
不,这不是一条规则,有时甚至很难/不可能实现。有一个入口和一个出口的代码更容易理解和调试。比较一下:
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;
}
现在我们只有一个出口点,而且(也考虑到变量名)更容易理解函数的作用。您还可以在最后一个返回上放置一个断点,并知道这是函数结束的地方,并且您肯定会命中它
此规则可能适用于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?
此外,多个出口可能是一些性能问题:当处理器运行当前命令时,它会在同一时间点处理几个下一个命令,并对它们执行一些操作。所以,如果你的代码有多个出口,比如:
if (condition)
return a;
DoSomething();
if (condition2)
return b;
如果第一个条件为真,则提取DoSomething()
命令将无效。事实上,使用分支预测仍然可以,但无论如何,最好记住这一点。
如果您想实现这一点,可以将返回值存储在变量中,但这不是真的。在同一个函数中可以有多个返回,而不会出现问题。
我个人并不反对提前退出,但我会提出第三种替代SingerOfTheFall的方案供考虑。
优点:
- 正常代码流(即无错误)干净地流经代码
- 没有机会失败一件"事情"并通过另一件"某物"无意中执行了一块代码
- 您可以在代码块上强制作用域;包括清除代码子块中使用的东西的作用域
缺点:
- 凹痕可以相加(尽管可以通过断开来减轻这种情况分为子功能)
- 如果编辑器中没有大括号匹配,则很难将错误与失败条件相匹配
int foo(){int errorCode=0;如果{//100行代码如果{//100行代码如果{//100行代码}其他{errorCode=-1;}}其他{errorCode=11;}}其他{errorCode=1;}return errorCode;}
- 为什么"do while"循环不断退出,即使条件计算结果为 false?
- 此代码是否违反一个定义规则
- 生成文件不对文件使用隐式规则
- 创建LinkedList退出,返回代码为-11(SIGSEGV)
- 当我在main中声明了我的2d数组时,为什么我的程序会退出
- 如何让LLDB在成功时退出,在失败时等待
- C++控制台应用程序阻止退出
- 变量可能尚未初始化[MIRA 2012规则9.1,强制性]
- 静态结构和一个定义规则
- 程序在执行程序的其余部分之前退出
- 构造函数在退出函数时无法初始化一个参数
- 为什么异常不退出程序?
- 我不断收到 [错误] ID 返回 1 退出状态错误,但看不到问题所在
- 退出简单while循环时出现问题
- 尽管遵循了规则,内存泄漏在哪里
- 使用vscode调试时,GDB意外退出
- 这是关于成员访问规则的正确摘要吗
- uint_not_usable_without_attribute在业力规则中使用数字生成器时静态断言失败
- 增强精神解析器规则以检测语句中的特殊结尾
- 单一进入/单一退出规则