336

我正在阅读我的 C++ 讲师的一些讲义,他写了以下内容:

  1. 使用缩进 // OK
  2. 永远不要依赖运算符优先级 - 始终使用括号 // OK
  3. 始终使用 { } 块 - 即使是单行 //也不行,为什么 ???
  4. 比较左侧的 const 对象 // OK
  5. 对 >= 0 的变量使用无符号 // 好技巧
  6. 删除后将指针设置为 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++;

使用第一版有什么好处?

4

24 回答 24

520

让我们尝试i在增加时也进行修改j

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
        i++;

不好了!来自 Python,这看起来不错,但实际上并非如此,因为它相当于:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

当然,这是一个愚蠢的错误,但即使是有经验的程序员也会犯。

ta.speot.is 的回答中指出了另一个很好的理由

我能想到的第三个是嵌套if的:

if (cond1)
   if (cond2) 
      doSomething();

现在,假设您现在想要doSomethingElse()什么时候cond1不满足(新功能)。所以:

if (cond1)
   if (cond2) 
      doSomething();
else
   doSomethingElse();

这显然是错误的,因为else关联与内部if.


编辑:由于这引起了一些关注,我将澄清我的观点。我正在回答的问题是:

使用第一版有什么好处?

我已经描述了。有一些好处。但是,IMO,“总是”规则并不总是适用。所以我不完全支持

始终使用 { } 块 - 即使是单行 // 不行,为什么 ???

我并不是说总是使用{}块。如果这是一个足够简单的条件和行为,请不要。如果您怀疑有人稍后可能会进来并更改您的代码以添加功能,请执行此操作。

于 2012-08-30T08:51:43.637 回答
332

如果您不使用{和 ,很容易意外更改带有注释的控制流}。例如:

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()并不总是被调用。

我们的代码库中有这个问题,有人在发布之前很快就禁用了某些功能。幸运的是,我们在代码审查中发现了它。

于 2012-08-30T08:52:58.373 回答
60

I have my doubts as to the competence of the lecturer. Considering his points:

  1. OK
  2. Would anyone really write (or want to read) (b*b) - ((4*a)*c)? Some precedences are obvious (or should be), and the extra parentheses just add to confusion. (On the other hand, you _should_ use the parentheses in less obvious cases, even if you know that they're not needed.)
  3. Sort of. There are two wide spread conventions for formatting conditionals and loops:
    if ( cond ) {
        code;
    }
    
    and:
    if ( cond )
    {
        code;
    }
    
    In the first, I'd agree with him. The opening { is not that visible, so it's best to assume it's always there. In the second, however, I (and most of the people I've worked with) have no problem with omitting the braces for a single statement. (Provided, of course, that the indentation is systematic and that you use this style consistently. (And a lot of very good programmers, writing very readable code, omit the braces even when formatting the first way.)
  4. NO. Things like if ( NULL == ptr ) are ugly enough to hinder readability. Write the comparisons intuitively. (Which in many cases results in the constant on the right.) His 4 is bad advice; anything which makes the code unnatural makes it less readable.
  5. NO. Anything but int is reserved for special cases. To experienced C and C++ programmers, the use of unsigned signals bit operators. C++ doesn't have a real cardinal type (or any other effective subrange type); unsigned doesn't work for numeric values, because of the promotion rules. Numerical values on which no arithmetic operations would make sense, like serial numbers, could presumably be unsigned. I'd argue against it, however, because it sends the wrong message: bitwise operations don't make sense either. The basic rule is that integral types are int, _unless_ there is a significant reason for using another type.
  6. NO. Doing this systematically is misleading, and doesn't actually protect against anything. In strict OO code, delete this; is often the most frequent case (and you can't set this to NULL), and otherwise, most delete are in destructors, so you can't access the pointer later anyway. And setting it to NULL doesn't do anything about any other pointers floating around. Setting the pointer systematically to NULL gives a false sense of security, and doesn't really buy you anything.

Look at the code in any of the typical references. Stroustrup violates every rule you've given except for the first, for example.

I'd suggest that you find another lecturer. One who actually knows what he's talking about.

于 2012-08-30T09:50:36.043 回答
48

所有其他答案都捍卫了讲师的规则 3。

让我说我同意你的观点:这条规则是多余的,我不会建议它。确实,如果您总是添加大括号,它理论上可以防止错误。另一方面,我在现实生活中从未遇到过这个问题:与其他答案所暗示的相反,一旦有必要,我就没有忘记添加大括号。如果您使用适当的缩进,很明显您需要在多个语句缩进时添加大括号。

“组件 10”的答案实际上突出了唯一可能真正导致错误的情况。但另一方面,无论如何,通过正则表达式替换代码总是需要非常小心。

现在让我们看看奖牌的另一面:总是使用大括号有什么缺点吗?其他答案只是忽略了这一点。但是有一个缺点:它占用了大量的垂直屏幕空间,这反过来又会使您的代码不可读,因为这意味着您必须滚动超出必要的范围。

考虑一个开头有很多保护子句的函数(是的,以下是糟糕的 C++ 代码,但在其他语言中这将是很常见的情况):

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
    {
        throw null_ptr_error("a");
    }
    if (b == nullptr)
    {
        throw null_ptr_error("b");
    }
    if (a == b)
    {
        throw logic_error("Cannot do method on identical objects");
    }
    if (not a->precondition_met())
    {
        throw logic_error("Precondition for a not met");
    }

    a->do_something_with(b);
}

这是可怕的代码,我强烈认为以下代码更具可读性:

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
        throw null_ptr_error("a");
    if (b == nullptr)
        throw null_ptr_error("b");
    if (a == b)
        throw logic_error("Cannot do method on identical objects");
    if (not a->precondition_met())
        throw logic_error("Precondition for a not met");

    a->do_something_with(b);
}

同样,短嵌套循环也受益于省略大括号:

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
        for (auto j = 0; j < a.h(); ++j)
            c(i, j) = a(i, j) + b(i, j);

    return c;
}

与之比较:

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
    {
        for (auto j = 0; j < a.h(); ++j)
        {
            c(i, j) = a(i, j) + b(i, j);
        }
    }

    return c;
}

第一个代码简洁;第二个代码臃肿。

是的,可以通过将左大括号放在前一行来在一定程度上缓解这种情况。所以:如果你坚持使用花括号,至少把左大括号放在前一行。

简而言之:不要编写占用屏幕空间的不必要的代码。


自从最初编写答案以来,我大多接受流行的代码样式并使用大括号,除非我可以将整个单个语句放在前一行。我仍然认为不使用冗余大括号通常更具可读性,而且我仍然从未遇到过由此引起的错误。

于 2012-08-30T12:17:12.387 回答
41

我正在处理的代码库中散布着对大括号有病态厌恶的人的代码,对于后来出现的人来说,它确实可以对可维护性产生影响。

我遇到的最常见的问题示例是:

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 秒,并且至少可以为您节省几分钟的混乱调试时间,您为什么使用减少歧义选项呢?对我来说似乎是虚假经济。

于 2012-08-30T10:27:51.403 回答
20

我的2c:

使用缩进

明显地

永远不要依赖运算符优先级 - 始终使用括号

我不会使用“从不”和“总是”这样的词,但总的来说,我认为这条规则很有用。在某些语言(Lisp、Smalltalk)中,这不是问题。

始终使用 { } 块 - 即使是单行

我从来没有这样做过,也从来没有遇到过一个问题,但我可以看到它对学生有什么好处,尤其是。如果他们以前学过 Python。

比较左侧的 const 对象

尤达条件?不谢谢。它损害了可读性。编译代码时只需使用最大警告级别。

对 >= 0 的变量使用无符号

好的。有趣的是,我听说 Stroustrup 不同意。

删除后将指针设置为 NULL - 双重删除保护

不好的建议!永远不要有指向已删除或不存在的对象的指针。

于 2012-08-30T12:19:46.467 回答
19

它更直观,更容易理解。它使意图明确。

{并且它确保当新用户可能在不知不觉中错过,}同时添加新的代码语句时代码不会中断。

于 2012-08-30T08:52:24.557 回答
14

为了补充上面非常明智的建议,我在重构一些代码时遇到的一个例子如下:我正在更改一个非常大的代码库以从一个 API 切换到另一个 API。第一个 API 调用设置公司 ID,如下所示:

setCompIds( const std::string& compId, const std::string& compSubId );

而替换需要两次调用:

setCompId( const std::string& compId );
setCompSubId( const std::string& compSubId );

我开始使用非常成功的正则表达式来改变它。我们还通过astyle传递代码,这确实使其更具可读性。然后,在审查过程的中途,我发现在某些有条件的情况下,它正在改变这一点:

if ( condition )
   setCompIds( compId, compSubId );

对此:

if ( condition )
   setCompId( compId );
setCompSubId( compSubId );

这显然不是所需要的。我不得不从头开始再次执行此操作,将替换完全视为一个块内,然后手动更改任何最终看起来很傻的东西(至少它不会不正确。)

我注意到astyle现在有一个选项--add-brackets,允许您在没有括号的地方添加括号,如果您发现自己处于与我相同的位置,我强烈建议您这样做。

于 2012-08-30T10:30:41.183 回答
8

{}除了一些明显的情况外,我在任何地方都在使用。单行是其中一种情况:

if(condition) return; // OK

if(condition) // 
   return;    // and this is not a one-liner 

在返回之前添加一些方法可能会伤害你。缩进表示满足条件时返回正在执行,但它会一直返回。

C# 中使用语句的其他示例

using (D d = new D())  // OK
using (C c = new C(d))
{
    c.UseLimitedResource();
}

这相当于

using (D d = new D())
{
    using (C c = new C(d))
    {
        c.UseLimitedResource();
    }
}
于 2012-08-30T13:44:46.483 回答
8

我能想到的最相关的例子:

if(someCondition)
   if(someOtherCondition)
      DoSomething();
else
   DoSomethingElse();

if将与哪个else配对?缩进意味着外部if获得了else,但这实际上并不是编译器会看到它的方式;内部会得到,而外部不会 。您必须知道这一点(或在调试模式下看到它的行为方式)才能通过检查找出为什么此代码可能没有达到您的期望。如果您了解 Python,它会变得更加混乱;在这种情况下,您知道缩进定义了代码块,因此您希望它根据缩进进行评估。然而,C# 并没有给出关于空白的快速翻转。ifelseif

现在,也就是说,我并不特别同意这个“总是使用括号”的规则。它使代码在垂直方向上非常嘈杂,从而降低了快速阅读代码的能力。如果声明是:

if(someCondition)
   DoSomething();

...那么它应该是这样写的。“总是使用括号”这句话听起来像“总是用括号括起来数学运算”。这会将非常简单的语句a * b + c / d变成((a * b) + (c / d)),引入丢失近括号的可能性(许多编码人员的祸根),为什么?操作顺序是众所周知的并且执行良好,因此括号是多余的。您只会使用括号来强制执行与通常应用不同的操作顺序:a * (b+c) / d例如。大括号类似;使用它们来定义在与默认值不同且不是“显而易见”(主观,但通常是常识)的情况下您想要做什么。

于 2012-08-30T14:31:50.630 回答
5

因为当你有两个没有 的语句时{},很容易错过一个问题。让我们假设代码看起来像这样。

int error = 0;
enum hash_type hash = SHA256;
struct hash_value *hash_result = hash_allocate();

if ((err = prepare_hash(hash, &hash_result))) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &client_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &server_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &exchange_params)) != 0)
    goto fail;
    goto fail;
if ((err = hash_finish(hash)) != 0)
    goto fail;

error = do_important_stuff_with(hash);

fail:
hash_free(hash);
return error;

看起来不错。它的问题很容易被忽略,尤其是当包含代码的函数更大时。问题是goto fail无条件运行。你可以很容易地想象这是多么令人沮丧(让你问为什么 lasthash_update总是失败,毕竟一切看起来都很好hash_update)。

但是,这并不意味着我要{}到处添加(在我看来,{}到处都看到很烦人)。虽然它可能会导致问题,但它从来没有在我自己的项目中出现过,因为我的个人编码风格禁止条件{}不在同一行(是的,我同意我的编码风格是非常规的,但我喜欢它,而且我为其他项目做贡献时使用项目的代码风格)。这使得下面的代码很好。

if (something) goto fail;

但不是下一个。

if (something)
    goto fail;
于 2014-05-25T18:58:37.563 回答
4

wrt 6:它更安全,因为删除空指针是无操作的。因此,如果您碰巧两次意外地经过该路径,则不会导致内存损坏释放空闲或已分配给其他东西的内存。

这主要是静态文件范围对象和单例的问题,它们的生命周期不是很清楚,并且已知在它们被销毁后会重新创建。

在大多数情况下,您可以通过使用 auto_ptrs 来避免这种情况

于 2012-08-30T09:30:56.810 回答
4

浏览答案,没有人明确说明我习惯的那种做法,讲述你的代码故事:

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放在j++同一应该向其他任何人发出信号,“我只希望这个块永远增加j。当然,这只有在行尽可能简单的情况下才值得,因为在此处设置断点,正如peri所提到的,不会很有用。

事实上,我刚刚运行了部分 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++;

这太糟糕了,我什至不知道它是否有效(按预期);不要这样做。新行和大括号有助于区分单独但相关的代码片段,就像散文中的逗号或分号一样。上面的块是一个非常长的句子,有几个从句和一些从不中断或暂停以区分不同部分的语句。

如果你真的想给别人发电报,那只能使用三元运算符或?:表格:

for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;

但这接近代码高尔夫,我认为这不是很好的做法(我不清楚是否应该将 j++ 放在一侧:)。注意我之前没有在 C++ 中运行过三元运算符,我不知道这是否有效,但它确实存在

简而言之:

想象一下您的读者(即维护代码的人)如何解释您的故事(代码)。尽可能让他们清楚。如果您知道新手编码员/学生正在维护这一点,甚至{}可能尽可能多地留下,以免他们感到困惑。

于 2012-09-02T13:29:34.027 回答
4

它通过明确定义循环和条件块的范围使您的代码更具可读性。它还可以使您免于意外错误。

于 2012-08-30T08:54:23.747 回答
4

我喜欢 Luchian 被接受的答案,事实上,我很难学到他是对的,所以我总是使用大括号,即使是单行块也是如此。但是,我个人在编写过滤器时会例外,就像您在示例中一样。这个:

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++;
}
于 2012-08-30T13:05:05.123 回答
4

帮助防止上述错误的一种选择是在不使用大括号时内联您想要发生的事情。当您尝试修改代码时,很难不注意到错误。

if (condition) doSomething();
else doSomethingElse();

if (condition) doSomething();
    doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong
于 2012-08-30T15:08:17.440 回答
3

添加花括号的另一个示例。一旦我在搜索错误并找到这样的代码:

void SomeSimpleEventHandler()
{
    SomeStatementAtTheBeginningNumber1;
    if (conditionX) SomeRegularStatement;
    SomeStatementAtTheBeginningNumber2;
    SomeStatementAtTheBeginningNumber3;
    if (!SomeConditionIsMet()) return;
    OtherwiseSomeAdditionalStatement1;
    OtherwiseSomeAdditionalStatement2;
    OtherwiseSomeAdditionalStatement3;
}

如果您逐行阅读该方法,您会注意到该方法中有一个条件,如果它不为真则返回。但实际上它看起来像 100 个其他简单的事件处理程序,它们根据某些条件设置一些变量。有一天,Fast Coder 进来并在方法末尾添加了额外的变量设置语句:

{
    ...
    OtherwiseSomeAdditionalStatement3;
    SetAnotherVariableUnconditionnaly;
}

结果 SetAnotherVariableUnconditionnaly 在 SomeConditionIsMet() 时执行,但快人没有注意到它,因为所有行的大小几乎相似,即使返回条件是垂直缩进的,它也不那么明显。

如果条件返回的格式如下:

if (!SomeConditionIsMet())
{
    return;
}

它非常引人注目,Fast Coder 一眼就能找到它。

于 2012-09-05T07:24:55.320 回答
3

如果您是编译器,则没有任何区别。两者都是一样的。

但是对于程序员来说,第一个更清晰,易于阅读且不易出错。

于 2012-08-30T08:55:13.347 回答
2

我认为第一个清楚然后第二个。它给人一种关闭指令的感觉,当代码变得复杂时,用很少的代码就{...}可以了,即使它是endifbegin...end

//first
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}


//second
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;
于 2012-09-28T21:06:41.100 回答
1

It is best to set the pointer to NULL when you have finished with it.

Here is an example why:

Class A does the following:

  1. Allocates a block of memory
  2. Then some time later, it delete this block of memory but does not set the pointer to NULL

Class B does the following

  1. Allocates memory (and in this instance it happens to be given the same memory block that was deleted by class A.)

At this point both Class A and Class B have pointers pointing to the same memory block, as far as Class A is concerned this block of memory does not exists because it is finished with it.

Consider the following problem:

What if there was a logic error in Class A which resulted in it writing to memory that now belongs to Class B?

In this particular instance, you will not get an bad access exception error because the memory address is legal, all the while class A is now effectively corrupting class B data.

Class B may eventually crash if it encounters unexpected values and when it does crash, chances are, you will spend quite a long time hunting this bug in class B when the problem is in class A.

If you had set the deleted memory pointer to NULL, you would have gotten an exception error as soon as any logic errors in Class A tried to write to NULL pointer.

If you are worried about the logic error with double delete when pointers are NULL for the second time, then add assert for this.

Also: If you are going to down vote, please explain.

于 2012-08-30T21:05:36.850 回答
1

总是有花括号是非常简单和强大的规则。但是,当有很多大括号时,代码可能看起来不优雅。如果规则允许省略花括号,那么应该有更详细的样式规则和更复杂的工具。否则很容易导致代码混乱和混乱(不优雅)。因此,将单一样式规则与其他样式指南和使用的工具分开可能是徒劳的。我将带来一些关于该规则#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();
}

else这在技术上违反了该规则,因为和之间没有大括号if。当尝试使用无意识的工具将其自动应用于代码库时,规则的这种二元性就会显现出来。的确,何必争论,只是让一个工具自动应用样式。

第二个细节(也经常被该规则的支持者忘记)是可能发生的错误绝不只是因为违反了该规则#3。实际上,这些几乎总是涉及违反规则#1(没有人反对)。再次从自动化工具的角度来看,制作一个在违反规则 #1 时立即抱怨的工具并不难,因此可以及时发现大部分错误。

第三个细节(该规则的反对者经常忘记)是由单个分号表示的空语句的混乱性质。大多数有一定经验的开发人员迟早会被唯一放错位置的分号或使用唯一分号编写的空语句弄糊涂。两个花括号而不是单个分号在视觉上更容易发现。

于 2013-07-30T12:04:04.260 回答
1

我不得不承认,这并不总是{}用于单行,但这是一个很好的做法。

  • 假设您编写了一个不带括号的代码,如下所示:

    for (int i = 0; i < 100; ++i) for (int j = 0; j < 100; ++j) DoSingleStuff();

过了一段时间,你想在j循环中添加一些其他的东西,你只是通过对齐来做到这一点而忘记添加括号。

  • 内存释放速度更快。假设您有很大的范围并在内部创建大数组(没有new它们在堆栈中)。在您离开范围后,这些数组将从内存中删除。但是有可能你在一个地方使用了那个数组,它会在堆栈中一段时间​​,成为某种垃圾。由于堆栈的大小有限且非常小,因此可能超过堆栈大小。因此,在某些情况下,最好编写{}以防止这种情况发生。注意这不是针对单行,而是针对这种情况:

    if (...) { //SomeStuff... {//我们没有 if、while 等。 //SomeOtherStuff } //SomeMoreStuff }

  • 第三种使用方法与第二种类似。它只是不是为了让堆栈更干净,而是为了打开一些功能。如果您mutex在长函数中使用,通常最好在访问数据之前和完成读/写之后锁定和解锁。注意,如果您有一些自己的classstruct使用constructordestructor锁定内存,则使用这种方式。

  • 更:

    if (...) if (...) SomeStuff(); 其他一些其他东西();//转到第二个 if,但 alligment 显示它在第一个...

总而言之,我不能说始终{}用于单行的最佳方式是什么,但这样做并没有什么坏处。

重要编辑 如果你为一行编写编译代码括号什么都不做,但如果你的代码将被解释,它会稍微减慢代码。非常轻微。

于 2013-07-30T08:09:22.267 回答
1

编写控制语句有多种可能的方法;它们的某些组合可以共存而不影响易读性,但其他组合会造成麻烦。样式

if (condition)
  statement;

将与其他一些编写控制语句的方式舒适地共存,但与其他方式不太好。如果多行控制语句写成:

if (condition)
{
  statement;
  statement;
}

那么,哪些if语句控制单行,哪些语句控制多行,在视觉上就会很明显。但是,如果多行if语句写成:

if (condition) {
  statement;
  statement;
}

那么有人试图在不添加必要的大括号的情况下扩展单语句if结构的可能性可能要高得多。

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相同缩进开头的语句将提供一个清晰的视觉指示,表明存在一些不寻常的东西。

于 2015-07-16T21:16:22.607 回答
0

在营地“总是使用牙套”10 年后,我最近转而不再使用它们。主要是受到鲍勃叔叔关于如何编写干净代码的一些论点的启发,我现在相信不使用大括号编写它们更具可读性。

if(guardClause)
      throw new SomeException(..)

Bob 大叔认为,在 if/for 语句中编写多行代码是一种潜在的可读性气味。

例如

if(someCondition)
{
   doTechnicalThingX();
   doTechnicalThingY();
   doTechnicalThingZ();
}

可能应该重构为

if(someCondition)
    doFunctionalThingA();

不知何故,不把大括号放在那里是有帮助的,因为我得到提醒,我在 if 块中编写了太多代码。

我确实相信代码风格是其他人提到的团队决定。

于 2021-08-12T12:10:11.350 回答