我在读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++;
使用第一个版本的好处是什么?
如果不使用{和},很容易意外地用注释更改控制流。例如:
if (condition)
do_something();
else
do_something_else();
must_always_do_this();
如果你用一行注释注释掉do_something_else(),你会得到这样的结果:
if (condition)
do_something();
else
//do_something_else();
must_always_do_this();
它会编译,但must_always_do_this()并不总是被调用。
我们在代码库中遇到了这个问题,有人在发布之前很快地禁用了一些功能。幸运的是,我们在代码审查中发现了它。
另一个添加花括号的例子。
有一次我在搜索一个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;
}
它是非常明显的,快速编码器一眼就能发现它。
在“总是使用牙套”的营地呆了10年之后,我最近开始不怎么使用牙套了。
主要是受到Bob叔叔关于如何编写干净代码的争论的启发,我现在相信不使用大括号编写代码更具有可读性。
if(guardClause)
throw new SomeException(..)
Bob大叔认为,在if/for语句中编写多行代码是潜在的可读性问题。
e.g.
if(someCondition)
{
doTechnicalThingX();
doTechnicalThingY();
doTechnicalThingZ();
}
应该被重构为
if(someCondition)
doFunctionalThingA();
对我来说,不把大括号放在那里是有帮助的,因为我得到提醒,我在if块中写了太多代码。
正如其他人所提到的,我相信代码风格是团队决策。
我喜欢卢钦公认的答案。事实上,我经过艰苦的学习才知道他是对的,所以我总是使用大括号,即使是单行块。然而,就我个人而言,我在编写过滤器时做了一个例外,就像您在示例中所做的那样。这样的:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0)
{
j++;
}
}
在我看来很混乱。它将'for'循环和'if'语句分离为单独的操作,而实际上你的目的是一个操作:计算所有能被2整除的整数。在更有表现力的语言中,可以这样写:
j = [1..100].filter(_%2 == 0).Count
在缺少闭包的语言中,过滤器不能用单个语句表示,而必须是一个for循环,后面跟着一个if语句。然而,这仍然是程序员心中的一个动作,我相信这应该反映在代码中,如下所示:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
{
j++;
}
我必须承认,我并不总是在单行中使用{},但这是一种很好的练习。
假设你写的代码没有括号,看起来像这样:
For (int I = 0;I < 100;+ + i)
For (int j = 0;J < 100;+ + j)
DoSingleStuff ();
过了一段时间,你想在j循环中添加一些其他东西,你只是通过对齐来做,忘记添加括号。
Memory deallocation is faster. Let’s say you have a big scope and create big arrays inside (without new so they are on the stack). Those arrays are removed from memory just after you leave the scope. But it is possible that you use that array in one place and it will be on the stack for a while and be some kind of rubbish. As a stack have limited and quite small size, it is possible to exceed the stack size. So in some cases it is better to write {} to preventing that. Note that this is not for a single line, but for such situations:
if (...)
{
//SomeStuff...
{//we have no if, while, etc.
//SomeOtherStuff
}
//SomeMoreStuff
}
The third way to use is similar to the second. It is just not to make the stack cleaner, but to open some functions. If you use mutex in long functions usually it is better to lock and unlock just before accessing data and just after finishing reading/writing that. Note: This way is used if you have some of your own class or struct with a constructor and destructor to lock memory.
What is more:
if (...)
if (...)
SomeStuff();
else
SomeOtherStuff(); // Goes to the second if, but alignment shows it is on first...
总而言之,我不能说,总是使用{}的最佳方式是单行,但这样做并没有什么不好。
如果你编写的是编译代码,但是如果你的代码是被解释的,那么你的代码就会非常非常慢。非常轻微。
总是使用花括号是一个非常简单而可靠的规则。然而,当有很多大括号时,代码可能看起来不优雅。
如果规则允许省略花括号,那么应该有更详细的样式规则和更复杂的工具。否则,它可能很容易导致混乱和混乱(不优雅)的代码。
因此,从使用的其他样式指南和工具中单独查看单个样式规则可能是徒劳的。我将介绍关于规则3的一些其他答案中没有提到的重要细节。
第一个有趣的细节是,该规则的大多数支持者都同意在else情况下违反该规则。换句话说,他们不希望得到这样的代码:
// Pedantic rule #3
if ( command == Eat )
{
eat();
}
else
{
if ( command == Sleep )
{
sleep();
}
else
{
if ( command == Drink )
{
drink();
}
else
{
complain_about_unknown_command();
}
}
}
相反,如果他们看到了,他们甚至会建议这样写:
// Not fully conforming to rule #3
if ( command == Eat )
{
eat();
}
else if ( command == Sleep )
{
sleep();
}
else if ( command == Drink )
{
drink();
}
else
{
complain_about_unknown_command();
}
从技术上讲,这违反了规则3,因为在else和if之间没有花括号,但大多数人认为它更明显,更容易阅读。当试图用不需要动脑筋的工具将规则自动应用到代码库时,规则的这种二元性就会显现出来。的确,为什么要争论呢?只需让一个工具自动应用样式。
第二个细节(也经常被该规则的支持者遗忘)是,可能发生的错觉错误绝不仅仅是因为违反了该规则#3。事实上,这些几乎总是涉及违反规则#1(没有人会争辩)。再一次从自动工具的角度来看,当规则1被违反时,制作一个立即抱怨(甚至修复)的工具并不难,因此可以及时发现大多数错误。
第三个细节(该规则的反对者经常忘记)是由一个分号表示的空语句的令人困惑的性质。大多数具有一定经验的开发人员迟早会被一个放错位置的分号或使用一个分号编写的空语句所迷惑。两个花括号比一个分号更容易识别。
因此,我的建议是,与其同意这些规则,不如同意自动格式化工具的配置,并使其成为构建过程的一部分。这些工具往往比这种争论的参与者更聪明。