我在读c++老师的课堂笔记,他是这样写的:
使用缩进// OK
永远不要依赖运算符优先级-总是使用括号// OK
总是使用{}块-即使是单行//不可以,为什么??
Const对象在比较的左边// OK
对>= 0的变量使用unsigned,这是个不错的技巧
删除后将指针设置为NULL -双重删除保护//不错
第三种方法我不清楚:放一行进去能得到什么
A{…} ?
例如,下面这段奇怪的代码:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0)
{
j++;
}
}
将其替换为:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
j++;
使用第一个版本的好处是什么?
另一个添加花括号的例子。
有一次我在搜索一个bug,发现了这样的代码:
void SomeSimpleEventHandler()
{
SomeStatementAtTheBeginningNumber1;
if (conditionX) SomeRegularStatement;
SomeStatementAtTheBeginningNumber2;
SomeStatementAtTheBeginningNumber3;
if (!SomeConditionIsMet()) return;
OtherwiseSomeAdditionalStatement1;
OtherwiseSomeAdditionalStatement2;
OtherwiseSomeAdditionalStatement3;
}
如果你逐行阅读这个方法,你会注意到方法中有一个条件,如果不为真就返回。但实际上,它看起来像其他100个简单的事件处理程序,它们基于某些条件设置一些变量。有一天,Fast Coder进来了,在方法的末尾添加了额外的变量设置语句:
{
...
OtherwiseSomeAdditionalStatement3;
SetAnotherVariableUnconditionally;
}
因此,setanothervariable无条件地在SomeConditionIsMet()时执行,但速度快的家伙没有注意到它,因为所有的行几乎都是相似的大小,即使返回条件垂直缩进,它也不那么明显。
如果条件返回的格式如下:
if (!SomeConditionIsMet())
{
return;
}
它是非常明显的,快速编码器一眼就能发现它。
我正在处理的代码库被那些病态地厌恶大括号的人分散在代码中,对于后来的人来说,它确实可以对可维护性产生影响。
我遇到的最常见的问题是:
if (really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
this_looks_like_a_then-statement_but_isn't;
所以当我出现并希望添加一个then语句时,如果我不小心,我很容易以这样的方式结束:
if (really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
{
this_looks_like_a_then-statement_but_isn't;
i_want_this_to_be_a_then-statement_but_it's_not;
}
考虑到添加大括号需要1秒左右的时间,并且可以为您节省至少几分钟的调试时间,为什么不选择减少歧义性呢?在我看来这是虚假的节约。
有许多可能的方法来编写控制语句;它们的某些组合可能共存而不损害易读性,但其他组合将引起麻烦。的风格
if (condition)
statement;
可以与其他一些编写控制语句的方法很好地共存,但与其他方法就不那么好了。如果多行受控语句被写成:
if (condition)
{
statement;
statement;
}
那么,从视觉上看,哪些if语句控制单行,哪些语句控制多行就很明显了。然而,多行If语句被写成:
if (condition) {
statement;
statement;
}
那么,如果构造不添加必要的大括号,那么试图扩展单个语句的可能性就会高得多。
如果代码库大量使用这种形式,那么下一行的单个语句if语句也可能有问题
if (condition) statement;
我个人的偏好是将语句放在自己的行上通常可以提高易读性,除非有许多if语句具有类似的控制块,例如。
if (x1 > xmax) x1 = xmax;
if (x1 < xmin) x1 = xmin;
if (x2 > xmax) x2 = xmax;
if (x2 < xmin) x2 = xmin;
etc.
在这种情况下,我通常会在这些if语句的前面和后面加上一个空行,以便在视觉上将它们与其他代码分开。在相同的缩进处使用一系列以if开头的语句将提供一个清晰的视觉指示,表明有不寻常的地方。
纵观答案,没有人明确地说明了我习惯的那种实践,讲述你的代码的故事:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0)
{
j++;
}
}
就变成:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0) j++;
}
将j++放在与if should相同的行上,向其他任何人发出信号:“我只想让这个块永远增加j”。当然,只有在行代码尽可能简单的情况下,这样做才有意义,因为在这里设置断点,正如前面提到的,并不是很有用。
事实上,我刚刚遇到了Twitter Storm API的一部分,它在Java中有这种“类型”代码。以下是执行代码的相关片段,在本幻灯片的第43页:
...
Integer Count = counts.get(word);
if (Count=null) count=0;
count++
...
for循环块中有两个东西,所以我不会内联那个代码。也就是说,从来没有:
int j = 0;
for (int i = 0 ; i < 100 ; ++i) if (i % 2 == 0) j++;
这很糟糕,我甚至不知道它是否有效(如预期的那样);不要这样做。新行和大括号有助于区分独立但相关的代码段,就像散文中的逗号或分号一样。上面的块是一个很长的句子,里面有几个从句和一些其他的语句,从来没有中断或暂停来区分不同的部分。
如果你真的想要向其他人发送一个只有一行的任务,请使用三元操作符或?:form:
for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;
但这几乎是代码高尔夫,我认为这不是很好的实践(我不清楚是否应该将j++放在:的一侧)。注意,我以前没有在c++中运行过三元运算符,我不知道这是否有效,但它确实存在。
简而言之:
想象一下您的读者(即维护代码的人)如何解释您的故事(代码)。让他们尽可能清楚地知道。如果您知道新手程序员/学生正在维护这个,甚至可能留下尽可能多的{},这样他们就不会感到困惑。
我能想到的最贴切的例子是:
if(someCondition)
if(someOtherCondition)
DoSomething();
else
DoSomethingElse();
Which if will the else be paired with? Indentation implies that the outer if gets the else, but that's not actually how the compiler will see it; the inner if will get the else, and the outer if doesn't. You would have to know that (or see it behave that way in debugging mode) to figure out by inspection why this code might be failing your expectations. It gets more confusing if you know Python; in that case you know that indentation defines code blocks, so you would expect it to evaluate according to the indentation. C#, however, doesn't give a flying flip about whitespace.
话虽如此,从表面上看,我并不特别同意这个“总是使用括号”的规则。它使代码的垂直噪声非常大,降低了快速读取代码的能力。如果语句是:
if(someCondition)
DoSomething();
... then it should be written just like this. The statement "always use brackets" sounds like "always surround mathematical operations with parentheses". That would turn the very simple statement a * b + c / d into ((a * b) + (c / d)), introducing the possibility of missing a close-paren (the bane of many a coder), and for what? The order of operations is well-known and well-enforced, so the parentheses are redundant. You'd only use parentheses to enforce a different order of operations than would normally be applied: a * (b+c) / d for instance. Block braces are similar; use them to define what you want to do in cases where it differs from the default, and is not "obvious" (subjective, but usually pretty common-sense).