我在读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。

其他回答

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

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

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

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

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

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

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

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

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 (condition) doSomething();
else doSomethingElse();

if (condition) doSomething();
    doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong

我认为第一个比第二个更清楚。它给人一种结束指令的感觉。使用短代码是可以的,但是当代码变得复杂时,{…}帮助很大,即使它是endif或begin…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++;