What's the purpose of using braces (i.e. {}) for a single-line if or loop?

前端 未结 23 1246
独厮守ぢ
独厮守ぢ 2020-11-28 00:33

I\'m reading some lecture notes of my C++ lecturer and he wrote the following:

  1. Use Indentation // OK
  2. Never rely on operator preced
相关标签:
23条回答
  • 2020-11-28 01:10

    There are a number of possible ways of writing control statements; certain combinations of them may co-exist without impairing legibility, but other combinations will cause trouble. The style

    if (condition)
      statement;
    

    will co-exist comfortably with some of the other ways of writing control statements, but not so well with others. If multi-line controlled statements are written as:

    if (condition)
    {
      statement;
      statement;
    }
    

    then it will be visually obvious which if statements control a single line and which ones control multiple lines. If, however, multi-line if statements are written as:

    if (condition) {
      statement;
      statement;
    }
    

    then the likelihood of someone trying to extend a single-statement if constructs without adding the necessary braces may be much higher.

    The single-statement-on-next line if statement may also be problematic if the codebase makes significant use of the form

    if (condition) statement;
    

    My own preference is that having the statement on its own line generally enhances legibility except in cases where there are many if statements with similar control blocks, e.g.

    if (x1 > xmax) x1 = xmax;
    if (x1 < xmin) x1 = xmin;
    if (x2 > xmax) x2 = xmax;
    if (x2 < xmin) x2 = xmin;
    etc.
    

    in which case I will generally precede and follow such groups of if statements with a blank line to visually separate them from other code. Having a range of statements that all start with if at the same indentation will then provide a clear visual indication that there's something unusual.

    0 讨论(0)
  • 2020-11-28 01:11

    I have to admit that not always use {} for single line, but it's a good practise.

    • Lets say you write a code without brackets that looks like this:

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

    And after some time you want to add in j loop some other stuff and you just do that by alignment and forget to add brackets.

    • Memory dealocation is faster. Lets say you have big scope and create big arrays inside (without new so they are in stack). Those arrays are removing from memory just after you leave scope. But it is possible that you use that array in one place and it will be in stack for a while and be some kind of rubbish. As a stack have limited and quite small size it is possible to exceed stack size. So in some cases it is better to write {} to prevent from that. NOTE this is not for single line but for such a situations:

      if (...) { //SomeStuff... {//we have no if, while, etc. //SomeOtherStuff } //SomeMoreStuff }

    • Third way to use it is similar with second. It just not to make 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 using if you have some your own class or struct with constructor and destructor to lock memory.

    • What is more:

      if (...) if (...) SomeStuff(); else SomeOtherStuff(); //goes to the second if, but alligment shows it is on first...

    All In All, I cannot say, what is the best way to always use {} for a single line but it is nothing bad to do that.

    IMPORTANT EDIT If you write compiling code brackets for a single line does nothing, but if your code will be interpretated it slowes code for very very slightly. Very slightly.

    0 讨论(0)
  • 2020-11-28 01:13

    To add to the very sensible suggestions above, one example I encountered while refactoring some code of where this becomes critical was as follows: I was altering a very large codebase to switch from one API to another. The first API had a call to set Company Id as follows:

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

    whereas the replacement needed two calls:

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

    I set about changing this using regular expressions which was very successful. We also passed the code through astyle, which really made it very much more readable. Then, part way through the review process, I discovered that in some conditional circumstances it was changing this:

    if ( condition )
       setCompIds( compId, compSubId );
    

    To this:

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

    which is clearly not what what was required. I had to go back to the beginning do this again by treating the replacement as completely within a block and then manually altering anything that ended up looking goofy (at least it wouldn't be incorrect.)

    I notice that astyle now has the option --add-brackets which allows you to add brackets where there are none and I strongly recommend this if you ever find yourself in the same position as I was.

    0 讨论(0)
  • 2020-11-28 01:13

    Another example of adding curly braces. Once I was searching for a bug and found such code:

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

    If you read the method line-by-line you will notice that there is a condition in the method that returns if it's not true. But actually it looks like 100 other simple event handlers that set some variables based on some conditions. And one day the Fast Coder comes in and adds additional variable setting statement at the end of the method:

    {
        ...
        OtherwiseSomeAdditionalStatement3;
        SetAnotherVariableUnconditionnaly;
    }
    

    As a result the SetAnotherVariableUnconditionnaly is executed when the SomeConditionIsMet(), but the fast guy didn't notice it because all lines are almost similar in size and even when the return condition is vertically indented it is not-so noticeable.

    If the conditional return is formatted like this:

    if (!SomeConditionIsMet())
    {
        return;
    }
    

    it is much noticeable and the Fast Coder will find it at a glance.

    0 讨论(0)
  • 2020-11-28 01:16

    All the other answers defend your lecturer’s rule 3.

    Let me say that I agree with you: the rule is redundant and I wouldn’t advise it. It’s true that it theoretically prevents errors if you always add curly brackets. On the other hand, I’ve never encountered this problem in real life: contrary to what other answers imply, I’ve not once forgotten to add the curly brackets once they became necessary. If you use proper indentation, it becomes immediately obvious that you need to add curly brackets once more than one statement is indented.

    The answer by “Component 10” actually highlights the only conceivable case where this could really lead to an error. But on the other hand, replacing code via regular expression always warrants enormous care anyway.

    Now let’s look at the other side of the medal: is there a disadvantage to always using curly brackets? The other answers simply ignore this point. But there is a disadvantage: it takes up a lot of vertical screen space, and this in turn can make your code unreadable because it means you have to scroll more than necessary.

    Consider a function with a lot of guard clauses at the beginning (and yes, the following is bad C++ code but in other languages this would be quite a common situation):

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

    This is horrible code, and I argue strongly that the following is vastly more readable:

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

    Similarly, short nested loops benefit from omitting the curly brackets:

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

    Compare with:

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

    The first code is concise; the second code is bloated.

    And yes, this can be mitigated to some extent by putting the opening brace on the previous line. But that would still be less readable than the code without any curly brackets.

    In short: don’t write unnecessary code which takes up screen space.

    0 讨论(0)
  • 2020-11-28 01:16

    I am using {} everywhere except a few cases where it's obvious. Single line is one of the cases:

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

    It may hurt you when you add some method before return. Indentation indicates that return is executing when condition is met, but it will return always.

    Other example in C# with using statment

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

    which is equivalent to

    using (D d = new D())
    {
        using (C c = new C(d))
        {
            c.UseLimitedResource();
        }
    }
    
    0 讨论(0)
提交回复
热议问题