我在读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++;

使用第一个版本的好处是什么?


当前回答

当你完成它时,最好将指针设置为NULL。

下面是一个例子:

A类行为如下:

分配一块内存 然后一段时间后,它删除这块内存,但不将指针设置为NULL

B类做以下事情

分配内存(在这个实例中,它得到的内存块恰好与类a删除的内存块相同)

在这一点上,类A和类B都有指向同一个内存块的指针,就类A而言,这块内存块不存在,因为它已经用完了。

考虑以下问题:

如果在类a中有一个逻辑错误,导致它写入现在属于类B的内存呢?

在这个特定的实例中,您不会得到一个糟糕的访问异常错误,因为内存地址是合法的,而类A现在有效地破坏了类B数据。

类B可能最终崩溃,如果它遇到意外值,当它崩溃时,很有可能,当问题出现在类a时,您将花费相当长的时间在类B中查找这个bug。

如果您已经将删除的内存指针设置为NULL,那么只要类A中的任何逻辑错误试图写入NULL指针,您就会得到一个异常错误。

如果您担心在指针第二次为NULL时使用双重删除会出现逻辑错误,那么可以为此添加assert。

其他回答

纵观答案,没有人明确地说明了我习惯的那种实践,讲述你的代码的故事:

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++中运行过三元运算符,我不知道这是否有效,但它确实存在。

简而言之:

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

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

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中指出了另一个很好的理由。的回答。

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

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

现在,假设您现在想在未满足cond1时执行somethingelse()(新特性)。所以:

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

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


编辑:既然这引起了一些关注,我将澄清我的观点。我想回答的问题是

使用第一个版本的好处是什么?

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

总是使用{}块-即使是单行//不可以,为什么??

我并不是说总是使用{}块。如果这是一个足够简单的条件和行为,那就不要。如果你怀疑有人可能稍后会来修改你的代码来增加功能,那就这么做。

总是使用花括号是一个非常简单而可靠的规则。然而,当有很多大括号时,代码可能看起来不优雅。

如果规则允许省略花括号,那么应该有更详细的样式规则和更复杂的工具。否则,它可能很容易导致混乱和混乱(不优雅)的代码。

因此,从使用的其他样式指南和工具中单独查看单个样式规则可能是徒劳的。我将介绍关于规则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被违反时,制作一个立即抱怨(甚至修复)的工具并不难,因此可以及时发现大多数错误。

第三个细节(该规则的反对者经常忘记)是由一个分号表示的空语句的令人困惑的性质。大多数具有一定经验的开发人员迟早会被一个放错位置的分号或使用一个分号编写的空语句所迷惑。两个花括号比一个分号更容易识别。

因此,我的建议是,与其同意这些规则,不如同意自动格式化工具的配置,并使其成为构建过程的一部分。这些工具往往比这种争论的参与者更聪明。

我能想到的最贴切的例子是:

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).

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

if(condition) return; // OK

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

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

c#中using语句的其他例子

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();
    }
}