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

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


当前回答

在“总是使用牙套”的营地呆了10年之后,我最近开始不怎么使用牙套了。 主要是受到Bob叔叔关于如何编写干净代码的争论的启发,我现在相信不使用大括号编写代码更具有可读性。

if(guardClause)
      throw new SomeException(..)

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

e.g.

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

应该被重构为

if(someCondition)
    doFunctionalThingA();

对我来说,不把大括号放在那里是有帮助的,因为我得到提醒,我在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被违反时,制作一个立即抱怨(甚至修复)的工具并不难,因此可以及时发现大多数错误。

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

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

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

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

Component 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;
}

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

是的,这可以通过在前一行上加上开大括号在一定程度上得到缓解。所以:如果你坚持使用大括号,至少要把左大括号放在前一行。

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


在最初写答案的时间里,我基本上接受了流行的代码风格,使用大括号,除非我能把整个语句放在前一行。我仍然认为不使用冗余大括号通常更具可读性,而且我仍然从未遇到过由此引起的错误。

我对这位讲师的能力表示怀疑。考虑到他 点:

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

查看任何典型参考文献中的代码。Stroustrup违反 除了第一条,你给出的每一条规则。

我建议你换个讲师。一个真正知道什么的人 他说的是。

它通过清楚地定义循环和条件块的范围,使您的代码更具可读性。它还可以避免你意外出错。

让我们尝试在增加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相关联。


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

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

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

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

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