你认为这种技术"不好"吗?

Ma9*_*9uS 21 c++ coding-style

有时您需要在某些非严重错误条件下跳过方法的一部分.您可以使用异常,但通常不建议在正常应用程序逻辑中使用异常,仅针对异常情况.

所以我做了这样的伎俩:

do
{
   bool isGood = true;

   .... some code

   if(!isGood)
       break;

   .... some more code

   if(!isGood)
       break;

   .... some more code

 } while(false);

 ..... some other code, which has to be executed.
Run Code Online (Sandbox Code Playgroud)

我使用一个"假"循环,它将运行一次,我可以通过中断继续中止它.

我的一些同事不喜欢这样,他们称之为"不良做法".我个人觉得这个方法很漂亮.但你怎么看?

pae*_*bal 47

不好的做法,这取决于.

我在这段代码中看到的是一种非常有创意的方式,可以用较少的硫磺味关键词来写"goto".

这个代码有多种替代方案,根据具体情况可以或不可能更好.

你的do/while解决方案

如果您有很多代码,那么您的解决方案很有趣,但会在某些有限的点评估此处理的"退出":

do
{
   bool isError = false ;

   /* some code, perhaps setting isError to true */
   if(isError) break ;
   /* some code, perhaps setting isError to true */
   if(isError) break ;
   /* some code, perhaps setting isError to true */
}
while(false) ;

// some other code   
Run Code Online (Sandbox Code Playgroud)

问题是你不能轻易使用"if(isError)break;" 是一个循环,因为它只会退出内循环,而不是你的do/while块.

当然,如果失败在另一个函数内部,函数必须返回某种错误代码,并且您的代码不能忘记正确解释错误代码.

我不会讨论使用ifs甚至嵌套ifs的替代方案,因为经过一些思考后,我发现它们比你自己的解决方案更差.

打电话给goto ...转到

也许你应该清楚地说明你正在使用goto的事实,并记录你选择这个解决方案的原因.

至少,它会显示代码可能出错,并提示审阅者验证或使您的解决方案无效.

你仍然必须打开一个块,而不是打破,使用goto.

{
   // etc.
   if(/*some failure condition*/) goto MY_EXIT ;
   // etc.

   while(/* etc.*/)
   {
      // etc.
      for(/* etc.*/)
      {
         // etc.
         if(/*some failure condition*/) goto MY_EXIT ;
         // etc.
      }
      // etc.
      if(/*some failure condition*/) goto MY_EXIT ;
      // etc.
   }

   // etc.
}

MY_EXIT:

// some other code   
Run Code Online (Sandbox Code Playgroud)

这样,当您通过goto退出块时,您无法绕过goto(C++禁止)的某个对象构造函数.

这个问题解决了从嵌套循环问题中退出的过程(并且使用goto退出嵌套循环是B. Stroustrup给出的一个例子,作为goto的有效使用),但它不能解决一些函数调用可能失败并被忽略的事实(因为有人未能正确测试他们的返回代码,如果有的话).

当然,现在,您可以从多个循环嵌套深度退出多个点,因此如果这是一个问题......

试着抓

如果代码不应该失败(因此,失败是例外),或者即使代码结构可能失败,但退出过于复杂,那么以下方法可能更清楚:

try
{
   // All your code
   // You can throw the moment something fails
   // Note that you can call functions, use reccursion,
   // have multiple loops, etc. it won't change
   // anything: If you want to exit the process,
   // then throw a MyExitProcessException exception.

   if(/* etc. */)
   {
      // etc.
      while(/* etc.*/)
      {
         // etc.
         for(/* etc.*/)
         {
            // etc.
            if(/*some failure condition*/) throw MyExitProcessException() ;
            // etc.
         }
         // etc.

         callSomeFunction() ;
         // the function will throw if the condition is met
         // so no need to test a return code

         // etc.
      }
      // etc.
   }

   // etc.
}
catch(const MyExitProcessException & e)
{
   // To avoid catching other exceptions, you should
   // define a "MyExitProcessException" exception
}

// some other code
Run Code Online (Sandbox Code Playgroud)

如果上面代码中的某些条件或上面代码调用的某些函数内部不满足,则抛出异常.

这比do/while解决方案稍微重一些,但具有相同的优点,甚至可以从内部循环或内部调用函数中止处理.

讨论

您的需求似乎来自于您可以执行复杂的过程(代码,函数调用,循环等),但您希望在某些条件下中断它(可能是失败,或者因为它比例外更快成功) .如果你能以不同的方式重写它,你应该这样做.但也许,没有其他办法.

让我们假设一下.

如果您可以使用try/catch对其进行编码,请执行以下操作:要中断复杂的代码,抛出异常是正确的解决方案(您可以在异常对象中添加失败/成功信息这一事实不应低估).之后您将获得更清晰的代码.

现在,如果你处于一个速度瓶颈,解决你的问题抛出异常作为退出并不是最快的方法.

没有人可以否认你的解决方案是一个美化的goto.不会有goto-spaghetti代码,因为do/while不会让你这样做,但它仍然是一个语义goto.这可能是一些人可能会发现此代码"不好"的原因:他们闻到了goto而没有清楚地找到它的关键字.

在这种情况下(并且在此性能,经过分析验证的情况下),您的解决方案看起来很好,并且比使用if的替代方案更好,但质量较差(恕我直言)比goto解决方案至少不会隐藏自身假循环背后.

结论

就我而言,我发现你的解决方案很有创意,但我会坚持抛出异常解决方案.

所以,按优先顺序排列:

  1. 使用try/catch
  2. 使用转到
  3. 使用do/while循环
  4. 使用ifs/nested ifs

  • 很有争议,但我争辩说"要打断一段复杂的代码,抛出异常是正确的解决方案." 你已经为"GOTO"提出了自己的观点,但我认为异常在语义上与特殊情况有关.不是控制流程的复杂性. (3认同)
  • 只是这样的细节答案的努力值得接受.我同意你不能突破内循环的观点.如果使用整数而不是bool作为中断条件(如所有值> 0表示错误),则仍然可以存储失败的函数返回代码 (2认同)

Pau*_*lin 31

你几乎只是将"goto"伪装成假循环.无论你喜欢和不喜欢,你都会使用一个真正毫无掩饰的goto.

就个人而言,我只是把它写成

bool isGood = true;

   .... some code

   if(isGood)
   {
   .... some more code
   }

   if(isGood)
   {
   .... some more code
   }
Run Code Online (Sandbox Code Playgroud)

  • 嵌套ifs会导致斜率编码,从而导致队友将铅笔塞入您的眼睛。 (2认同)

g .*_*g . 24

为什么要使用假循环?你可以用一种方法做同样的事情,它可能不会被认为是一种"坏习惯",因为它更受期待.

someMethod()
{
   .... some code

   if(!isGood)
       return;

   .... some more code

   if(!isGood)
       return;

   .... some more code

 }
Run Code Online (Sandbox Code Playgroud)

  • 如果您不使用 RAII/共享指针,则多次返回是不好的。然而,正如 g 所说,使用这些工具,早期编程绝对有用,并且比深度嵌套逻辑更可取。 (2认同)
  • 我认为将多次返回的混淆等同于原始代码的混淆是不正确的,原始代码结合了复杂的控制流逻辑(无论是他的“假循环”还是按照 Tomblin 的回答重写)与域逻辑('...更多代码这里...')。+1 介绍功能。 (2认同)

Pra*_*mod 12

在一个难以识别的习语中,你有复杂的非线性控制流.所以,是的,我认为这种技术很糟糕.

花一些时间试图弄清楚这是否可以写得更好一些可能是值得的.


Wed*_*dge 11

这是令人费解和困惑的,我会立即废弃它.

考虑这个选择:

private void DoSomething()
{
   // some code
   if (some condition)
   {
      return;
   }
   // some more code
   if (some other condition)
   {
      return;
   }
   // yet more code
}
Run Code Online (Sandbox Code Playgroud)

还要考虑将上面的代码分解为多个方法.


jon*_*nii 8

public bool Method1(){ ... }
public bool Method2(){ ... }

public void DoStuff(){
    bool everythingWorked = Method1() && Method2();
    ... stuff you want executed always ...
}
Run Code Online (Sandbox Code Playgroud)

其工作原因是由于称为短路逻辑.如果Method1返回false,则不会调用Method2.

这还有一个额外的好处,你可以将你的方法分成更小的块,这将更容易进行单元测试.


Aar*_*ron 5

您要做的是非本地故障恢复.这goto是为了什么.用它.(实际上,这是异常处理的目的 - 但如果你不能使用它,'goto'或'setjmp/longjmp'是下一个最好的东西).

这种模式,if(succeeded(..))模式和'goto cleanup',都是3在语义上和结构上都是等价的.使用代码项目中最常见的一个.一致性很有价值.

我会提醒if(failed(..)) break;你,如果你试图嵌套循环,你会产生一个惊人的结果:

do{
   bool isGood = true;
   .... some code
   if(!isGood)
       break;
   .... some more code
   for(....){
      if(!isGood)
          break; // <-- OOPS, this will exit the 'for' loop, which 
                 //  probably isn't what the author intended
      .... some more code
   }
} while(false);
..... some other code, which has to be executed.
Run Code Online (Sandbox Code Playgroud)

既没有goto cleanupif(succeeded(..))没有这个惊喜,所以我鼓励使用这两个中的一个.


Mec*_*cki 5

基本上你刚刚描述了goto.我一直在C中使用goto.我不认为它不好,除非你用它来模拟一个循环(从来没有这样做过!).我在C中使用goto的典型用法是模拟异常(C没有例外):

// Code

if (bad_thing_happened) goto catch;

// More code

if (bad_thing_happened) goto catch;

// Even more code

finally:

// This code is executed in any case
// whether we have an exception or not,
// just like finally statement in other
// languages

return whatever;

catch:

// Code to handle bad error condition

// Make sure code tagged for finally
// is executed in any case
goto finally;
Run Code Online (Sandbox Code Playgroud)

除了catch和最后有相反顺序的事实,我不明白为什么这个代码应该是因为它使用goto,如果真正的try/catch/finally代码完全像这样工作并且不使用goto.这是没有意义的.因此,我无法确定为什么您的代码应被标记为BAD.